Compose Navigation:为导航目的地设置条件式转场动画
针对你需求的场景——Category屏幕与底部导航目的地间用默认/无动画,与子屏幕间用滑动动画,可以通过以下方式在转场回调中判断导航的来源与目标:
核心思路
利用Compose Navigation提供的转场回调参数(targetDestination、initialState)直接获取导航动作的目标屏幕和来源屏幕信息,无需依赖返回栈的实时状态(哪怕来源屏幕已被弹出)。
具体实现步骤
1. 统一定义路由常量
先把所有屏幕的路由抽成常量,避免拼写错误:
const val HOME_SCREEN = "home" const val CATEGORY_SCREEN = "category" const val CATEGORY_DETAIL_SCREEN = "category_detail"
2. 在NavHost中配置条件转场
在composable函数的转场参数中,通过targetDestination(目标屏幕)和initialState(导航前的栈条目,即来源屏幕)做判断:
val navController = rememberNavController() NavHost( navController = navController, startDestination = HOME_SCREEN ) { // 首页(带底部导航) composable(HOME_SCREEN) { HomeScreen(onNavigateToCategory = { // 底部导航跳转Category时,保持栈的正确状态 navController.navigate(CATEGORY_SCREEN) { popUpTo(navController.graph.startDestinationId) { saveState = true } launchSingleTop = true restoreState = true } }) } // Category屏幕 composable( route = CATEGORY_SCREEN, // 退出动画:只有前往子屏幕时用滑动 exitTransition = { if (targetDestination.route == CATEGORY_DETAIL_SCREEN) { slideOutHorizontally(targetOffsetX = { -it }) // 向左滑出 } else { fadeOut() // 返回底部导航时用淡出(或用null表示无动画) } }, // 回退进入动画:只有从子屏幕返回时用滑动 popEnterTransition = { if (initialState.destination.route == CATEGORY_DETAIL_SCREEN) { slideInHorizontally(initialOffsetX = { -it }) // 向左滑入 } else { fadeIn() // 从底部导航进入时用淡入 } } ) { CategoryScreen(onItemClick = { itemId -> navController.navigate("$CATEGORY_DETAIL_SCREEN/$itemId") }) } // Category子屏幕(详情页) composable( route = "$CATEGORY_DETAIL_SCREEN/{itemId}", arguments = listOf(navArgument("itemId") { type = NavType.StringType }), // 进入动画:只有从Category进入时用滑动 enterTransition = { if (initialState.destination.route == CATEGORY_SCREEN) { slideInHorizontally(initialOffsetX = { it }) // 向右滑入 } else { fadeIn() } }, // 回退退出动画:只有返回Category时用滑动 popExitTransition = { if (targetDestination.route == CATEGORY_SCREEN) { slideOutHorizontally(targetOffsetX = { it }) // 向右滑出 } else { fadeOut() } } ) { backStackEntry -> val itemId = backStackEntry.arguments?.getString("itemId") ?: "" CategoryDetailScreen(onBackClick = { navController.popBackStack() }) } }
关键参数说明
targetDestination:在exitTransition和popExitTransition中可用,直接指向导航动作的目标屏幕路由,用来判断要去的是哪个页面。initialState:在enterTransition和popEnterTransition中可用,代表导航动作发生前的栈条目,哪怕来源屏幕已经被弹出,依然能通过它获取来源屏幕的路由,完美解决你提到的“前序屏幕已弹出无法检查返回栈”的问题。
优化建议
如果有多个子屏幕或者复杂的判断逻辑,可以把判断逻辑抽成扩展函数,让代码更简洁:
private fun NavBackStackEntry?.isCategoryScreen() = this?.destination?.route == CATEGORY_SCREEN private fun NavDestination?.isCategoryDetailScreen() = this?.route?.startsWith(CATEGORY_DETAIL_SCREEN) == true
内容的提问来源于stack exchange,提问作者galaxigirl
相关产品推荐
相关产品推荐

