Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TNT-177] 바텀 네비게이션 구현 #76

Merged
merged 4 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package co.kr.tnt.designsystem.component.bottombar

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import co.kr.tnt.core.designsystem.R
import co.kr.tnt.designsystem.theme.TnTTheme

@Composable
fun <Tab : BottomTab> TnTBottomBar(
bottomTabs: List<Tab>,
currentTab: Tab?,
onClickTab: (tab: Tab) -> Unit,
modifier: Modifier = Modifier,
) {
Row(modifier = modifier) {
bottomTabs.forEach { tab ->
Button(
onClick = { onClickTab(tab) },
modifier = Modifier.weight(1f),
colors = ButtonColors(
containerColor = TnTTheme.colors.commonColors.Common0,
contentColor = TnTTheme.colors.commonColors.Common0,
disabledContainerColor = TnTTheme.colors.commonColors.Common0,
disabledContentColor = TnTTheme.colors.commonColors.Common0,
),
) {
TnTBottomTab(
bottomTab = tab,
isSelected = currentTab == tab,
)
}
}
}
}

@Composable
private fun TnTBottomTab(
isSelected: Boolean,
bottomTab: BottomTab,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
painter = painterResource(bottomTab.icon),
contentDescription = null,
tint = if (isSelected) {
TnTTheme.colors.neutralColors.Neutral800
} else {
TnTTheme.colors.neutralColors.Neutral300
},
)
Text(
text = bottomTab.contentDescription,
style = TnTTheme.typography.label2Medium,
fontSize = 12.sp,
color = if (isSelected) {
TnTTheme.colors.neutralColors.Neutral800
} else {
TnTTheme.colors.neutralColors.Neutral400
},
modifier = Modifier.padding(top = 6.dp),
)
}
}

@Preview(showBackground = true)
@Composable
private fun TnTBottomBarPreview() {
val home = object : BottomTab {
override val icon: Int
get() = R.drawable.ic_navbar_home
override val contentDescription: String
get() = "홈"
}

val myPage = object : BottomTab {
override val icon: Int
get() = R.drawable.ic_navbar_mypage
override val contentDescription: String
get() = "내 정보"
}

val tabs = listOf(home, myPage)

TnTTheme {
TnTBottomBar(
bottomTabs = tabs,
currentTab = home,
onClickTab = { },
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package co.kr.tnt.designsystem.component.bottombar

interface BottomTab {
val icon: Int
val contentDescription: String
}
18 changes: 18 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_navbar_feedback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M9,4H6C4.895,4 4,4.895 4,6V19C4,20.105 4.895,21 6,21H18C19.105,21 20,20.105 20,19V6C20,4.895 19.105,4 18,4H15V5C15,5.552 14.552,6 14,6H10C9.448,6 9,5.552 9,5V4ZM7.75,11.25C7.75,10.56 8.31,10 9,10H15C15.69,10 16.25,10.56 16.25,11.25C16.25,11.94 15.69,12.5 15,12.5H9C8.31,12.5 7.75,11.94 7.75,11.25ZM9,14C8.31,14 7.75,14.56 7.75,15.25C7.75,15.94 8.31,16.5 9,16.5H15C15.69,16.5 16.25,15.94 16.25,15.25C16.25,14.56 15.69,14 15,14H9Z"
android:fillColor="#D4D4D4"
android:fillType="evenOdd"/>
<path
android:pathData="M9,4H9.375V3.625H9V4ZM15,4V3.625H14.625V4H15ZM6,4.375H9V3.625H6V4.375ZM4.375,6C4.375,5.103 5.103,4.375 6,4.375V3.625C4.688,3.625 3.625,4.688 3.625,6H4.375ZM4.375,19V6H3.625V19H4.375ZM6,20.625C5.103,20.625 4.375,19.897 4.375,19H3.625C3.625,20.312 4.688,21.375 6,21.375V20.625ZM18,20.625H6V21.375H18V20.625ZM19.625,19C19.625,19.897 18.897,20.625 18,20.625V21.375C19.312,21.375 20.375,20.312 20.375,19H19.625ZM19.625,6V19H20.375V6H19.625ZM18,4.375C18.897,4.375 19.625,5.103 19.625,6H20.375C20.375,4.688 19.312,3.625 18,3.625V4.375ZM15,4.375H18V3.625H15V4.375ZM15.375,5V4H14.625V5H15.375ZM14,6.375C14.759,6.375 15.375,5.759 15.375,5H14.625C14.625,5.345 14.345,5.625 14,5.625V6.375ZM10,6.375H14V5.625H10V6.375ZM8.625,5C8.625,5.759 9.241,6.375 10,6.375V5.625C9.655,5.625 9.375,5.345 9.375,5H8.625ZM8.625,4V5H9.375V4H8.625ZM9,9.625C8.103,9.625 7.375,10.352 7.375,11.25H8.125C8.125,10.767 8.517,10.375 9,10.375V9.625ZM15,9.625H9V10.375H15V9.625ZM16.625,11.25C16.625,10.352 15.898,9.625 15,9.625V10.375C15.483,10.375 15.875,10.767 15.875,11.25H16.625ZM15,12.875C15.898,12.875 16.625,12.148 16.625,11.25H15.875C15.875,11.733 15.483,12.125 15,12.125V12.875ZM9,12.875H15V12.125H9V12.875ZM7.375,11.25C7.375,12.148 8.103,12.875 9,12.875V12.125C8.517,12.125 8.125,11.733 8.125,11.25H7.375ZM8.125,15.25C8.125,14.767 8.517,14.375 9,14.375V13.625C8.103,13.625 7.375,14.352 7.375,15.25H8.125ZM9,16.125C8.517,16.125 8.125,15.733 8.125,15.25H7.375C7.375,16.147 8.103,16.875 9,16.875V16.125ZM15,16.125H9V16.875H15V16.125ZM15.875,15.25C15.875,15.733 15.483,16.125 15,16.125V16.875C15.898,16.875 16.625,16.147 16.625,15.25H15.875ZM15,14.375C15.483,14.375 15.875,14.767 15.875,15.25H16.625C16.625,14.352 15.898,13.625 15,13.625V14.375ZM9,14.375H15V13.625H9V14.375Z"
android:fillColor="#D4D4D4"/>
<path
android:pathData="M10,2L14,2A1,1 0,0 1,15 3L15,5A1,1 0,0 1,14 6L10,6A1,1 0,0 1,9 5L9,3A1,1 0,0 1,10 2z"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#D4D4D4"/>
</vector>
31 changes: 31 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_navbar_members.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M9,4C7.343,4 6,5.343 6,7C6,8.657 7.343,10 9,10C10.657,10 12,8.657 12,7C12,5.343 10.657,4 9,4Z"
android:fillColor="#D4D4D4"
android:fillType="evenOdd"/>
<path
android:pathData="M6.75,7C6.75,5.757 7.757,4.75 9,4.75V3.25C6.929,3.25 5.25,4.929 5.25,7H6.75ZM9,9.25C7.757,9.25 6.75,8.243 6.75,7H5.25C5.25,9.071 6.929,10.75 9,10.75V9.25ZM11.25,7C11.25,8.243 10.243,9.25 9,9.25V10.75C11.071,10.75 12.75,9.071 12.75,7H11.25ZM9,4.75C10.243,4.75 11.25,5.757 11.25,7H12.75C12.75,4.929 11.071,3.25 9,3.25V4.75Z"
android:fillColor="#D4D4D4"/>
<path
android:pathData="M17,6C15.895,6 15,6.895 15,8C15,9.105 15.895,10 17,10C18.105,10 19,9.105 19,8C19,6.895 18.105,6 17,6Z"
android:fillColor="#D4D4D4"
android:fillType="evenOdd"/>
<path
android:pathData="M15.75,8C15.75,7.31 16.31,6.75 17,6.75V5.25C15.481,5.25 14.25,6.481 14.25,8H15.75ZM17,9.25C16.31,9.25 15.75,8.69 15.75,8H14.25C14.25,9.519 15.481,10.75 17,10.75V9.25ZM18.25,8C18.25,8.69 17.69,9.25 17,9.25V10.75C18.519,10.75 19.75,9.519 19.75,8H18.25ZM17,6.75C17.69,6.75 18.25,7.31 18.25,8H19.75C19.75,6.481 18.519,5.25 17,5.25V6.75Z"
android:fillColor="#D4D4D4"/>
<path
android:pathData="M8.571,12.5C5.494,12.5 3,15.046 3,18.188C3,18.912 3.588,19.5 4.313,19.5H13.688C14.412,19.5 15,18.912 15,18.188C15,15.046 12.506,12.5 9.429,12.5H8.571Z"
android:fillColor="#D4D4D4"
android:fillType="evenOdd"/>
<path
android:pathData="M3.75,18.188C3.75,15.446 5.923,13.25 8.571,13.25V11.75C5.066,11.75 2.25,14.647 2.25,18.188H3.75ZM13.688,18.75H4.313V20.25H13.688V18.75ZM9.429,13.25C12.077,13.25 14.25,15.446 14.25,18.188H15.75C15.75,14.647 12.934,11.75 9.429,11.75V13.25ZM8.571,13.25H9.429V11.75H8.571V13.25ZM13.688,20.25C14.827,20.25 15.75,19.327 15.75,18.188H14.25C14.25,18.498 13.998,18.75 13.688,18.75V20.25ZM2.25,18.188C2.25,19.327 3.173,20.25 4.313,20.25V18.75C4.002,18.75 3.75,18.498 3.75,18.188H2.25Z"
android:fillColor="#D4D4D4"/>
<path
android:pathData="M16,13.2C16,12.502 16.57,11.926 17.253,12.066C20.085,12.642 22.571,15.104 22.571,18.188C22.571,19.223 21.732,20.063 20.696,20.063H20C18.895,20.063 17.909,19.172 17.709,18.086C17.652,17.773 17.583,17.413 17.5,17C17.237,15.684 16.835,14.905 16.513,14.462C16.245,14.093 16,13.655 16,13.2Z"
android:fillColor="#D4D4D4"
android:fillType="evenOdd"/>
</vector>
Original file line number Diff line number Diff line change
@@ -1,36 +1,15 @@
package co.kr.tnt.trainee.main

import android.annotation.SuppressLint
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import co.kr.tnt.designsystem.component.bottombar.TnTBottomBar
import co.kr.tnt.designsystem.theme.TnTTheme
import co.kr.tnt.navigation.Route
import co.kr.tnt.trainee.home.navigation.traineeHomeNavGraph
import co.kr.tnt.trainee.mypage.navigation.traineeMyPageNavGraph
import co.kr.tnt.trainee.notification.navigation.navigateToTraineeNotification
Expand All @@ -41,10 +20,13 @@ internal fun TraineeMainRoute(
navigateToConnect: () -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
navController: NavHostController = rememberNavController(),
) {
val state = rememberTraineeMainState(
startDestination = TraineeMainTab.HOME.baseRoute,
)

TraineeMainScreen(
navController = navController,
state = state,
navigateToConnect = navigateToConnect,
navigateToLogin = navigateToLogin,
navigateToWebView = navigateToWebView,
Expand All @@ -54,36 +36,31 @@ internal fun TraineeMainRoute(
@Composable
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
private fun TraineeMainScreen(
navController: NavHostController,
state: TraineeMainState,
navigateToConnect: () -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
) {
val navController = state.navController

Scaffold(
containerColor = TnTTheme.colors.commonColors.Common0,
modifier = Modifier.fillMaxSize(),
bottomBar = {
TraineeMainBottomBar(
navController = navController,
onClickTab = { tab ->
navController.navigate(
route = tab.route,
navOptions = navOptions {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
},
)
},
)
if (state.shouldShowBottomBar) {
TnTBottomBar(
modifier = Modifier.navigationBarsPadding(),
bottomTabs = state.mainTabs,
currentTab = state.currentMainTab,
onClickTab = state::navigateMainTab,
)
}
},
) { innerPadding ->
NavHost(
modifier = Modifier.padding(innerPadding),
navController = navController,
startDestination = Route.TraineeMainTab.Home,
startDestination = state.startDestination,
) {
traineeHomeNavGraph(
navigateToNotification = navController::navigateToTraineeNotification,
Expand All @@ -101,86 +78,3 @@ private fun TraineeMainScreen(
}
}
}

@Composable
private fun TraineeMainBottomBar(
navController: NavHostController,
onClickTab: (tab: TraineeMainTab) -> Unit,
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route

Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.background(TnTTheme.colors.commonColors.Common0)
.fillMaxWidth()
.padding(horizontal = 24.dp)
.navigationBarsPadding(),
) {
TraineeMainTab.entries.forEach { tab ->
val selected = currentRoute?.contains(tab.route.toString()) == true
BottomAppBarItem(
tab = tab,
selected = selected,
onClickTab = onClickTab,
)
}
}
}

@Composable
private fun RowScope.BottomAppBarItem(
tab: TraineeMainTab,
selected: Boolean,
onClickTab: (tab: TraineeMainTab) -> Unit,
) {
Button(
onClick = { onClickTab(tab) },
modifier = Modifier.weight(1f),
colors = ButtonColors(
containerColor = TnTTheme.colors.commonColors.Common0,
contentColor = TnTTheme.colors.commonColors.Common0,
disabledContainerColor = TnTTheme.colors.commonColors.Common0,
disabledContentColor = TnTTheme.colors.commonColors.Common0,
),
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
painter = painterResource(tab.icon),
contentDescription = null,
tint = if (selected) {
TnTTheme.colors.neutralColors.Neutral800
} else {
TnTTheme.colors.neutralColors.Neutral300
},
)
Text(
text = tab.contentDescription,
style = TnTTheme.typography.label2Medium,
fontSize = 12.sp,
color = if (selected) {
TnTTheme.colors.neutralColors.Neutral800
} else {
TnTTheme.colors.neutralColors.Neutral400
},
modifier = Modifier.padding(top = 6.dp),
)
}
}
}

@Preview
@Composable
private fun TraineeMainBottomPreview() {
TnTTheme {
TraineeMainBottomBar(
onClickTab = {},
navController = rememberNavController(),
)
}
}
Loading