Compose Navigation中如何重写指定导航操作的转场动画?
问题
我开发的Android应用采用Compose Navigation及多模块嵌套导航。XML导航里能给指定导航操作加专属转场动画,但Compose里好像只能在composable/navigation级别设置。
外层NavHost有自己的转场配置:
NavHost( navController = navController, startDestination = ..., route = ..., enterTransition = ..., exitTransition = ..., popEnterTransition = ..., popExitTransition = .., ) { composable(MainRoutes.Onboarding.route) { OnboardingScreen() } addItemNavigation( navController, onItemAdded = { // 我想重写此导航操作的退出转场 navController.navigate(OnboardingRoutes.Onboarding.route) } ) }
嵌套导航有另一组转场配置:
fun NavGraphBuilder.addItemNavigation( navController: NavController, onItemAdded: () -> Unit ) { navigation( route = ..., startDestination = ... ) { composable( route = ..., enterTransition = ...// 与外层不同的转场 exitTransition = ... // 与外层不同的转场 popEnterTransition = ...// 与外层不同的转场 popExitTransition = ...// 与外层不同的转场 ) { // ... } } }
现在的问题是:希望onItemAdded回调触发的导航用不同的退出转场,但addItemNavigation不了解外层导航,没法不硬编码路由字符串就筛选composable级别的转场,有没有办法实现?
解决方案
1. 用navigate的extras传递转场标识
通过NavOptionsBuilder的extras携带自定义参数,在外层NavHost的转场逻辑里动态判断切换动画:
首先修改onItemAdded中的导航调用,添加标识参数:
onItemAdded = { navController.navigate(OnboardingRoutes.Onboarding.route) { val extras = Bundle().apply { putBoolean("USE_SPECIAL_EXIT", true) } this.extras = extras } }
然后在外层NavHost的转场配置中读取参数:
NavHost( navController = navController, startDestination = ..., route = ..., enterTransition = { defaultEnterTransition }, exitTransition = { val useSpecial = navBackStackEntry.arguments?.getBoolean("USE_SPECIAL_EXIT") ?: false if (useSpecial) { // 自定义特殊退出转场 slideOutHorizontally(targetOffsetX = { -it }, animationSpec = tween(300)) } else { defaultExitTransition } }, popEnterTransition = ..., popExitTransition = .., ) { // ... 原有代码 }
2. 把导航逻辑交还给外层控制
修改嵌套导航的函数定义,让它接收一个“特殊导航”的高阶函数,而不是自己处理navigate,这样外层就能完全控制转场:
更新addItemNavigation:
fun NavGraphBuilder.addItemNavigation( navController: NavController, onNavigateToOnboardingWithSpecialTransition: () -> Unit ) { navigation( route = ..., startDestination = ... ) { composable( route = ..., enterTransition = ..., exitTransition = ..., popEnterTransition = ..., popExitTransition = ... ) { // 触发操作时调用外层传入的逻辑 AddItemButton(onClick = { onNavigateToOnboardingWithSpecialTransition() }) } } }
外层通过状态变量控制转场,用完后重置:
val useSpecialExit = rememberSaveable { mutableStateOf(false) } NavHost( navController = navController, startDestination = ..., route = ..., enterTransition = ..., exitTransition = { if (useSpecialExit.value) { // 特殊退出转场 fadeOut(animationSpec = tween(200)) } else { defaultExitTransition }.also { // 重置状态,避免影响后续导航 useSpecialExit.value = false } }, popEnterTransition = ..., popExitTransition = .., ) { composable(MainRoutes.Onboarding.route) { OnboardingScreen() } addItemNavigation( navController, onNavigateToOnboardingWithSpecialTransition = { useSpecialExit.value = true navController.navigate(OnboardingRoutes.Onboarding.route) } ) }
3. 自定义Navigator.Extras传递转场配置
进阶方案,自定义一个专门传递转场的Extras类,避免硬编码参数键名:
class TransitionExtras(val exitTransition: AnimatedContentTransitionScope<*>.() -> EnterTransition?) : Navigator.Extras
导航时传入自定义Extras:
onItemAdded = { navController.navigate(OnboardingRoutes.Onboarding.route) { extras = TransitionExtras { // 自定义退出转场 slideOutVertically(targetOffsetY = { it }, animationSpec = tween(300)) } } }
外层NavHost中读取Extras并应用转场:
exitTransition = { val transitionExtras = navController.currentDestination?.extras as? TransitionExtras transitionExtras?.exitTransition?.invoke(this) ?: defaultExitTransition }
内容的提问来源于stack exchange,提问作者ascu
相关产品推荐
相关产品推荐

