You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 11:02:04