내가 구현하고 싶은 내비게이션의 조건은 다음과 같다.
구성 요소 관리: 데이터를 BottomNavItem으로 통합하여 재사용성 강화.
상태 기반 UI: 선택 상태에 따라 아이콘과 텍스트 스타일 변경.
효율적 화면 전환: popUpTo와 restoreState를 활용해 상태 복원 및 인스턴스 중복 생성 방지.
피그마에서 디자인한 하단 내비게이션 바
Scaffold를 사용해 기본 레이아웃을 구성하며, 주요 컴포넌트는 다음과 같다.
Jetpack Compose의 NavHost와 NavController를 활용하여 여러 화면을 연결하며, 각 화면은 composable로 정의된다.
다음 코드는 MainActivity에서 호출되는 Main 함수 내에 작성한다.
💡 주요 구현 포인트
- startDestination: 앱의 첫 시작 화면 정의
MainActivity.kt
val navController = rememberNavController()
Scaffold(
bottomBar = { BottomAppBar(navController = navController) }
) { padding ->
NavHost(
navController = navController,
startDestination = "home",
modifier = Modifier.padding(padding)
) {
composable("home") { HomeScreen(navController) }
composable("schedule") { ScheduleScreen(navController) }
composable("add") { AddScreen(navController) }
composable("my") { MyScreen(navController) }
}
}
각 내비게이션 아이템을 담는 전체적인 바 컴포넌트 구현
popUpTo와 restoreState를 활용해 상태 복원 및 인스턴스 중복 생성 방지
💡 주요 구현 포인트
- currentRoute: 현재 선택된 탭 확인
- popUpTo: 이전 화면 상태 복원
- weight: 선택된 탭의 크기를 상대적으로 크게 설정
@Composable
fun BottomAppBar(navController: NavController) {
val items = listOf(
BottomNavItem("home", R.drawable.ic_home, R.drawable.ic_home_selected, "Home"),
BottomNavItem("schedule", R.drawable.ic_schedule, R.drawable.ic_schedule_selected, "Schedule"),
BottomNavItem("add", R.drawable.ic_add, R.drawable.ic_add_selected, "Add"),
BottomNavItem("my", R.drawable.ic_my, R.drawable.ic_my_selected, "My")
)
val navBackStackEntry = navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry.value?.destination?.route ?: "home"
Row(
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
.background(MaterialTheme.colorScheme.tertiary),
verticalAlignment = Alignment.CenterVertically
) {
items.forEach { item ->
// currentRoute: 현재 선택된 탭 확인
val isSelected = currentRoute == item.route
BottomNavItem(
item = item,
isSelected = isSelected,
onClick = {
navController.navigate(item.route) {
// popUpTo: 이전 화면 상태 복원
popUpTo(navController.graph.startDestinationId) { saveState = true }
launchSingleTop = true
restoreState = true
}
},
modifier = Modifier.weight(if (isSelected) 2f else 1f) // 선택된 탭은 더 넓게 표시
)
}
}
}
item 데이터를 BottomNavItem으로 통합하여 재사용성 강화
💡 주요 구현 포인트
- 선택 상태 스타일링: 선택된 아이템은 배경색, 텍스트 스타일, 아이콘 색상이 변경
- 텍스트 표시 조건: 선택된 상태일 때만 라벨 텍스트가 표시
@Composable
fun BottomNavItem(
item: BottomNavItem,
isSelected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.clickable(onClick = onClick)
.padding(horizontal = 8.dp, vertical = 8.dp)
.background(
color = if (isSelected) Color(0x1AFFC700) else Color.Transparent,
shape = RoundedCornerShape(12.dp)
)
.height(48.dp)
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Icon(
painter = painterResource(id = if (isSelected) item.selectedIcon else item.icon),
contentDescription = null,
tint = if (isSelected) Color(0xFFFFC107) else BottomItemColor,
modifier = Modifier.size(24.dp)
)
if (isSelected) {
Spacer(modifier = Modifier.width(8.dp))
Text(
text = item.label,
color = Color(0xFFFFC107),
fontSize = 15.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
)
}
}
}
피그마에서 디자인한 대로 구현 완료
