Jetpack Compose:导航动画时不带动Scaffold的TopBar与BottomBar
解决Accompanist导航动画时TopBar/BottomBar跟随动效的问题
当前你遇到的问题是每个页面都嵌套了完整的Scaffold(包含TopBar和BottomBar),导致导航动画作用在整个页面上,顶部和底部栏跟着一起动。要实现仅中间内容动画、栏保持静态,核心思路是把Scaffold提到导航组件的外面,让TopBar和BottomBar作为全局固定布局,NavHost只负责中间内容区域的切换。
具体修改步骤:
1. 重构父组件,整合Scaffold与导航
把原来每个页面里的Scaffold移到NavHost的父容器中,让TopBar、BottomBar和NavHost同属一个Scaffold:
@Composable fun MainScreen(appState: YourAppState) { val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed)) val navController = appState.navController // 联动BottomBar选中状态与当前路由 val selectedRoute = navController.currentBackStackEntryAsState().value?.destination?.route val selectedIndex = remember { mutableStateOf(2) } LaunchedEffect(selectedRoute) { selectedIndex.value = when(selectedRoute) { SETTINGS_SCREEN -> 0 TEACHER_SCREEN -> 1 LOG_SCREEN -> 2 else -> 2 } } Scaffold( scaffoldState = scaffoldState, topBar = { TopAppBar( title = { Text( text = "BrahmaPass", fontSize = 30.sp, fontFamily = poppinsFamily, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() ) }, backgroundColor = MaterialTheme.colors.primary ) }, content = { padding -> // NavHost仅负责中间内容切换,动画只作用于此区域 NavHost( navController = navController, startDestination = LOG_SCREEN, modifier = Modifier.padding(padding) ) { composable( LOG_SCREEN, enterTransition = { slideInHorizontally(initialOffsetX = { 1000 }) } ) { LogScreenContent() // 仅保留页面核心内容 } composable( TEACHER_SCREEN, enterTransition = { slideInHorizontally(initialOffsetX = { 1000 }) } ) { TeacherScreenContent() // 其他页面同理修改 } composable( SETTINGS_SCREEN, enterTransition = { slideInHorizontally(initialOffsetX = { 1000 }) } ) { SettingsScreenContent() } } }, bottomBar = { BottomBar( selectedIndex = selectedIndex.value, onItemClick = { index -> selectedIndex.value = index when(index) { 0 -> appState.navigateAndPopUp(SETTINGS_SCREEN, LOG_SCREEN) 1 -> appState.navigateAndPopUp(TEACHER_SCREEN, LOG_SCREEN) 2 -> appState.navigateAndPopUp(LOG_SCREEN, TEACHER_SCREEN) // 按需调整回退逻辑 } } ) } ) }
2. 简化单个页面组件,仅保留核心内容
原来的LogScreen去掉Scaffold、TopBar和BottomBar,只保留中间的内容布局:
@Composable fun LogScreenContent( modifier: Modifier = Modifier, viewModel: LoginViewModel = hiltViewModel() ) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier .padding(15.dp) .fillMaxSize() ) { Text( "Exit Log", fontSize = 30.sp, fontFamily = com.example.brahmapassv3.screens.home.poppinsFamily, fontWeight = FontWeight.Medium, textAlign = TextAlign.Center, ) // 页面原有其他内容继续保留 } }
3. 调整BottomBar,与导航路由联动
把BottomBar的选中状态和导航路由绑定,无需每个页面单独维护:
@Composable fun BottomBar( selectedIndex: Int, onItemClick: (Int) -> Unit, ) { BottomNavigation(elevation = 10.dp) { BottomNavigationItem( icon = { Icon(imageVector = Icons.Default.Settings, contentDescription = "设置") }, selected = selectedIndex == 0, onClick = { onItemClick(0) } ) BottomNavigationItem( icon = { Icon(imageVector = Icons.Default.Home, contentDescription = "首页") }, selected = selectedIndex == 1, onClick = { onItemClick(1) } ) BottomNavigationItem( icon = { Icon(imageVector = Icons.Default.DateRange, contentDescription = "日志") }, selected = selectedIndex == 2, onClick = { onItemClick(2) } ) } }
核心原理
通过将Scaffold提升到NavHost外层,TopBar和BottomBar成为全局固定元素,导航动画仅作用于NavHost内部的页面内容,完美实现“栏不动、内容动”的效果。
内容的提问来源于stack exchange,提问作者erdaifuu
相关产品推荐
相关产品推荐

