Skip to content

Commit

Permalink
Merge pull request #76 from YAPP-Github/feature/TNT-177
Browse files Browse the repository at this point in the history
[TNT-177] 바텀 네비게이션 구현
  • Loading branch information
hoyahozz authored Feb 10, 2025
2 parents be6da08 + 8d8b503 commit 0c3fce9
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 180 deletions.
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

0 comments on commit 0c3fce9

Please sign in to comment.