From 3ebd828d8d06c0773c1e6c33cf667084271eb4b6 Mon Sep 17 00:00:00 2001 From: sanjeok77 <1+sanjeok77@noreply.localhost> Date: Thu, 20 Aug 2026 23:43:16 +0000 Subject: [PATCH] v0.2.7: Seamless Docked Bottom Navigation Bar Redesign --- .../alarm/presentation/main/MainScreen.kt | 105 +++++++++++------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/hotdeal/alarm/presentation/main/MainScreen.kt b/app/src/main/java/com/hotdeal/alarm/presentation/main/MainScreen.kt index 2286c5d..1a2de1b 100644 --- a/app/src/main/java/com/hotdeal/alarm/presentation/main/MainScreen.kt +++ b/app/src/main/java/com/hotdeal/alarm/presentation/main/MainScreen.kt @@ -11,11 +11,13 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -24,7 +26,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat -import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hotdeal.alarm.presentation.components.PermissionDialog import com.hotdeal.alarm.presentation.components.elasticPressClickable import com.hotdeal.alarm.presentation.deallist.DealListScreen @@ -33,13 +34,12 @@ import com.hotdeal.alarm.ui.theme.CornerRadius import kotlinx.coroutines.launch /** - * One UI 9 Fluid Navigation Framework - * 한 손 조작성을 극대화한 하단 캡슐 내비게이션 바 및 화면 전환 + * One UI 9 Docked Navigation Framework + * 바닥까지 완벽하게 밀착된 일체형 네이티브 하단 바 */ @Composable fun MainScreen(viewModel: MainViewModel) { val context = LocalContext.current - val uiState by viewModel.uiState.collectAsStateWithLifecycle() var showPermissionDialog by remember { mutableStateOf(false) } val notificationPermissionLauncher = rememberLauncherForActivityResult( @@ -67,22 +67,23 @@ fun MainScreen(viewModel: MainViewModel) { Scaffold( bottomBar = { - // One UI 9 Fluid Bottom Navigation Bar - Surface( + // One UI 9 Docked Bottom Navigation Bar (바닥과 완벽 일체화) + Column( modifier = Modifier .fillMaxWidth() - .navigationBarsPadding(), - color = MaterialTheme.colorScheme.surface, - tonalElevation = 3.dp, - border = androidx.compose.foundation.BorderStroke( - 0.8.dp, - MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.25f) - ) + .background(MaterialTheme.colorScheme.surface) ) { - NavigationBar( - containerColor = Color.Transparent, - tonalElevation = 0.dp, - modifier = Modifier.height(64.dp) + // 상단 초미세 헤어라인 구분선 + HorizontalDivider( + thickness = 0.6.dp, + color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.35f) + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .height(54.dp), + verticalAlignment = Alignment.CenterVertically ) { val navItems = listOf( Triple(0, "핫딜 피드", Icons.Filled.LocalFireDepartment to Icons.Outlined.LocalFireDepartment), @@ -93,39 +94,65 @@ fun MainScreen(viewModel: MainViewModel) { val isSelected = pagerState.currentPage == pageIndex val (selectedIcon, unselectedIcon) = icons - NavigationBarItem( - selected = isSelected, - onClick = { - coroutineScope.launch { - pagerState.animateScrollToPage(pageIndex) + val itemColor = if (isSelected) { + MaterialTheme.colorScheme.primary + } else { + MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.65f) + } + + Box( + modifier = Modifier + .weight(1f) + .fillMaxHeight() + .elasticPressClickable { + coroutineScope.launch { + pagerState.animateScrollToPage(pageIndex) + } + }, + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + // 선택 시 상단 미니 캡슐 인디케이터 + if (isSelected) { + Box( + modifier = Modifier + .width(20.dp) + .height(3.dp) + .clip(CornerRadius.shapePill) + .background(MaterialTheme.colorScheme.primary) + ) + Spacer(modifier = Modifier.height(3.dp)) + } else { + Spacer(modifier = Modifier.height(6.dp)) } - }, - icon = { + Icon( imageVector = if (isSelected) selectedIcon else unselectedIcon, contentDescription = label, - modifier = Modifier.size(24.dp) + tint = itemColor, + modifier = Modifier.size(22.dp) ) - }, - label = { + + Spacer(modifier = Modifier.height(2.dp)) + Text( text = label, style = MaterialTheme.typography.labelSmall.copy( - fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium, - fontSize = 11.sp - ) + fontSize = 11.sp, + fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium + ), + color = itemColor ) - }, - colors = NavigationBarItemDefaults.colors( - selectedIconColor = MaterialTheme.colorScheme.primary, - selectedTextColor = MaterialTheme.colorScheme.primary, - unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f), - unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f), - indicatorColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.6f) - ) - ) + } + } } } + + // 시스템 제스처 네비게이션 바 영역 패딩 (배경색은 일체형으로 유지) + Spacer(modifier = Modifier.navigationBarsPadding()) } }, contentWindowInsets = WindowInsets(0, 0, 0, 0)