Jetpack Compose ModalBottomSheet内实现嵌套导航方案咨询
问题原因
你当前的实现把列表页、详情页都注册成了根导航栈里平级的bottomSheet类型目的地,Accompanist的BottomSheetNavigator会为每一个bottomSheet目的地单独创建、弹出独立的BottomSheet实例,跳转时自然会先关闭上一个弹窗,再弹出承载新页面的全新弹窗,根本不可能实现单弹窗内的页面切换。
实现方案
只给整个BottomSheet业务流程注册唯一一个根级bottomSheet路由,在这个路由对应的Composable内部,单独创建一个仅在BottomSheet生命周期内生效的嵌套导航栈,用这个内部导航栈控制列表、详情页的切换,就不会触发外层BottomSheet的关闭和重建。
完整代码实现
- 简化外层根导航的BottomSheet注册逻辑,只保留一个入口路由:
val modalBottomSheetState = rememberModalBottomSheetState( initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true ) val bottomSheetNavigator = remember(modalBottomSheetState) { BottomSheetNavigator(sheetState = modalBottomSheetState) } val rootNavController = rememberNavController() ModalBottomSheetLayout( bottomSheetNavigator = bottomSheetNavigator, content = { Scaffold( bottomBar = { // 原有底部导航栏逻辑不变 }, content = { innerPadding -> NavHost( navController = rootNavController, startDestination = vmMain.getStartDestination(), modifier = Modifier.padding(innerPadding) ) { // 原有普通页面导航配置不变 navigation( startDestination = OnboardingDirections.appPreview().destination, route = OnboardingDirections.root().destination ) { composable(OnboardingDirections.appPreview().destination) { IntroScreen() } // 其他原有composable目的地 } // 仅注册一个BottomSheet入口,不要给详情页单独注册bottomSheet路由 bottomSheet(route = FeatureA.root().destination) { FeatureAFlowContent() } } } ) } )
- 实现BottomSheet内部的嵌套导航逻辑,独立管理列表-详情页切换:
@Composable fun FeatureAFlowContent() { // 这个导航控制器仅服务于BottomSheet内部页面切换,不会触发外层弹窗状态变化 val innerNavController = rememberNavController() NavHost( navController = innerNavController, startDestination = "feature_list" ) { // 初始列表页 composable(route = "feature_list") { ScreenA( onItemClick = { selectedId -> // 点击列表项走内部导航,不会关闭重建BottomSheet innerNavController.navigate("feature_detail/$selectedId") } ) } // 详情页 composable( route = "feature_detail/{itemId}", arguments = listOf(navArgument("itemId") { type = NavType.StringType }) ) { entry -> val itemId = entry.arguments?.getString("itemId").orEmpty() ScreenB( itemId = itemId, onBack = { // 内部页面返回走内部导航栈,BottomSheet本身保持展开状态 innerNavController.popBackStack() }, onCloseSheet = { // 需要完全关闭整个BottomSheet时,再操作外层根导航栈或者sheetState rootNavController.popBackStack() } ) } } }
注意点
- 禁止把BottomSheet内部的列表、详情页注册为外层根NavHost的
bottomSheet类型目的地,否则还是会触发新弹窗弹出 - 内部页面间的返回、跳转全部操作
innerNavController,只有需要彻底关闭整个BottomSheet的时候,才调用外层根导航的返回方法,或者直接控制modalBottomSheetState执行隐藏 - 该方案下整个列表到详情的切换全程在同一个BottomSheet实例内完成,不会出现弹窗关了再开的割裂动画,完全匹配预期交互
内容的提问来源于stack exchange,提问作者Tomasz Sieber
相关产品推荐
相关产品推荐

