使用PaddingValues导致AnimatedVisibility隐藏底部栏后跳动问题求助
问题描述
为避免底部导航栏与页面内容重叠,我在内容中使用Modifier.padding(PaddingValues)进行布局,但隐藏底部栏后,AnimatedVisibility会出现持续几秒的跳动效果;若不使用PaddingValues,跳动效果会消失,但我必须保留该padding来避免内容被底部栏遮挡。请问是否有可行的解决办法?

MainScreen代码:
@Composable fun MainScreenView() { val navController = rememberNavController() val bottomBarState = rememberSaveable { (mutableStateOf(true)) } val navBackStackEntry by navController.currentBackStackEntryAsState() when(navBackStackEntry?.destination?.route){ DetailsNavScreen.WallpapersDetail.route -> {bottomBarState.value = false} // on this screen bottom bar should be hidden else -> bottomBarState.value = true } com.google.accompanist.insets.ui.Scaffold(bottomBar = {BottomNavigationBar(navController = navController,bottomBarState) }) { Box(modifier = Modifier.padding(it))// Using Padding Values here to prevent //Bottom bar from overlaping screen content.. Note the jumppy effect goes off if paddingValues is not used { NavigationGraph(navController = navController) } } }
BottomNavigation代码:
@Composable fun BottomNavigationBar(navController: NavController,bottomBarState: MutableState<Boolean>) { AnimatedVisibility(visible = bottomBarState.value, enter = slideInVertically { it }, exit = slideOutVertically { it } , content = { BottomNavigation { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentDestination = navBackStackEntry?.destination items.forEach { screen -> BottomNavigationItem( icon = { Icon( painterResource(id = screen.icon), contentDescription = stringResource(screen.resourceID) ) }, label = { Text(stringResource(screen.resourceID)) }, selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true, onClick = { navController.navigate(screen.route) { // Pop up to the start destination of the graph to // avoid building up a large stack of destinations // on the back stack as users select items popUpTo(navController.graph.findStartDestination().id) { saveState = true } // Avoid multiple copies of the same destination when // reselecting the same item launchSingleTop = true // Restore state when reselecting a previously selected item restoreState = true } } ) } } }) }
解决办法
方案1:固定底部栏占位空间,避免PaddingValues突变
问题核心是底部栏隐藏时,Scaffold返回的PaddingValues会瞬间消失,导致内容区域padding突变引发跳动。给底部栏套一个固定高度的Box,让Scaffold始终计算底部占位高度:
@Composable fun BottomNavigationBar(navController: NavController, bottomBarState: MutableState<Boolean>) { // 始终保留BottomNavigation的高度占位 Box(modifier = Modifier.height(BottomNavigationDefaults.Height)) { AnimatedVisibility( visible = bottomBarState.value, enter = slideInVertically { it }, exit = slideOutVertically { it } ) { BottomNavigation { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentDestination = navBackStackEntry?.destination items.forEach { screen -> BottomNavigationItem( icon = { Icon( painterResource(id = screen.icon), contentDescription = stringResource(screen.resourceID) ) }, label = { Text(stringResource(screen.resourceID)) }, selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true, onClick = { navController.navigate(screen.route) { popUpTo(navController.graph.findStartDestination().id) { saveState = true } launchSingleTop = true restoreState = true } } ) } } } } }
这样Scaffold返回的PaddingValues不会变化,内容区域padding稳定,同时底部栏的滑入滑出动画正常执行。
方案2:动态平滑过渡内容区域padding
如果不想保留底部占位空间,手动根据底部栏状态计算padding并添加动画过渡:
@Composable fun MainScreenView() { val navController = rememberNavController() val bottomBarState = rememberSaveable { mutableStateOf(true) } val navBackStackEntry by navController.currentBackStackEntryAsState() when(navBackStackEntry?.destination?.route){ DetailsNavScreen.WallpapersDetail.route -> bottomBarState.value = false else -> bottomBarState.value = true } // 让底部padding随底部栏状态平滑变化 val dynamicBottomPadding = animateDpAsState( targetValue = if (bottomBarState.value) BottomNavigationDefaults.Height else 0.dp, animationSpec = tween(300) // 和底部栏动画时长保持一致 ) com.google.accompanist.insets.ui.Scaffold( bottomBar = { BottomNavigationBar(navController = navController, bottomBarState) } ) { innerPadding -> // 合并系统内边距和动态计算的底部padding val combinedPadding = innerPadding.copy(bottom = innerPadding.bottom + dynamicBottomPadding.value) Box(modifier = Modifier.padding(combinedPadding)) { NavigationGraph(navController = navController) } } }
通过animateDpAsState让padding平滑过渡,避免突然变化引发的跳动,同时保证内容不会被底部栏遮挡。
方案3:用AnimatedContent替代AnimatedVisibility
AnimatedContent能让Scaffold感知到底部栏高度的平滑变化,从而同步调整内容padding:
@Composable fun BottomNavigationBar(navController: NavController, bottomBarState: MutableState<Boolean>) { AnimatedContent( targetState = bottomBarState.value, transitionSpec = { if (targetState) { slideInVertically { it } with slideOutVertically { -it } } else { slideInVertically { -it } with slideOutVertically { it } }.using(SizeTransform(clip = false)) } ) { isVisible -> if (isVisible) { BottomNavigation { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentDestination = navBackStackEntry?.destination items.forEach { screen -> BottomNavigationItem( icon = { Icon( painterResource(id = screen.icon), contentDescription = stringResource(screen.resourceID) ) }, label = { Text(stringResource(screen.resourceID)) }, selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true, onClick = { navController.navigate(screen.route) { popUpTo(navController.graph.findStartDestination().id) { saveState = true } launchSingleTop = true restoreState = true } } ) } } } else { Spacer(modifier = Modifier.height(0.dp)) } } }
SizeTransform确保底部栏高度变化时,Scaffold能同步平滑调整内容区域的padding,彻底消除跳动。
内容的提问来源于stack exchange,提问作者Sky
相关产品推荐
相关产品推荐

