Jetpack Compose中进入退出动画失效问题排查
问题解决:Compose导航中遮罩淡入淡出动画不生效
问题根源
直接通过navController.currentBackStackEntry?.destination?.route判断AnimatedVisibility的可见性,无法让Compose正确追踪状态变化。当导航到ScreenTwo时,ScreenOne的currentBackStackEntry仍指向自身条目,路由状态的切换时机和Compose重组不同步,导致遮罩瞬间显示/隐藏,跳过动画。
修复方案
方案1:在ScreenOne内监听路由变化
通过currentBackStackEntryFlow实时监听导航路由变化,将路由状态转为Compose可感知的状态变量,以此控制遮罩的显示动画:
@Composable fun ScreenOne(navController: NavController){ val currentRoute = remember { mutableStateOf("ScreenOne") } // 监听路由变化,更新本地状态 LaunchedEffect(navController) { navController.currentBackStackEntryFlow.collect { entry -> currentRoute.value = entry.destination.route ?: "ScreenOne" } } Column( modifier = Modifier .fillMaxSize() .background(Color.Red), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ){ Button( onClick = { navController.navigate("ScreenTwo") } ) { Text(text = "Go To Screen Two") } } AnimatedVisibility( visible = currentRoute.value == "ScreenTwo", enter = fadeIn(animationSpec = tween(500)), exit = fadeOut(animationSpec = tween(500)) ) { Box( modifier = Modifier .fillMaxSize() .background(Color.Black.copy(alpha = 0.5f)) ) } }
方案2:全局维护路由状态并传递
在MyApp中统一监听路由变化,将当前路由作为参数传递给ScreenOne:
@Composable fun MyApp(){ val navController = rememberNavController() val currentRoute = remember { mutableStateOf("ScreenOne") } LaunchedEffect(navController) { navController.currentBackStackEntryFlow.collect { entry -> currentRoute.value = entry.destination.route ?: "ScreenOne" } } NavHost( navController = navController, startDestination = "ScreenOne", enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None } ){ composable( "ScreenOne", enterTransition = { slideIntoContainer( animationSpec = tween(500), towards = AnimatedContentTransitionScope.SlideDirection.End ) }, exitTransition = { slideOutOfContainer( animationSpec = tween(1000), towards = AnimatedContentTransitionScope.SlideDirection.Start ) } ){ ScreenOne(navController, currentRoute.value) } composable( "ScreenTwo", enterTransition = { slideIntoContainer( animationSpec = tween(500), towards = AnimatedContentTransitionScope.SlideDirection.Start ) }, exitTransition = { slideOutOfContainer( animationSpec = tween(500), towards = AnimatedContentTransitionScope.SlideDirection.End ) } ){ ScreenTwo(navController) } } } @Composable fun ScreenOne(navController: NavController, currentRoute: String){ Column( modifier = Modifier .fillMaxSize() .background(Color.Red), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ){ Button( onClick = { navController.navigate("ScreenTwo") } ) { Text(text = "Go To Screen Two") } } AnimatedVisibility( visible = currentRoute == "ScreenTwo", enter = fadeIn(animationSpec = tween(500)), exit = fadeOut(animationSpec = tween(500)) ) { Box( modifier = Modifier .fillMaxSize() .background(Color.Black.copy(alpha = 0.5f)) ) } }
优化建议
可以将遮罩的动画时长与ScreenOne的滑出动画时长对齐(当前ScreenOne的exitTransition是1000ms),让动画更协调:
enter = fadeIn(animationSpec = tween(1000)), exit = fadeOut(animationSpec = tween(1000))
内容的提问来源于stack exchange,提问作者Raj
相关产品推荐
相关产品推荐

