Jetpack Compose:多页面共享ViewModel并在超出作用域时销毁
原生Jetpack Compose解决方案:基于嵌套导航图管理表单ViewModel生命周期
针对你的场景,不需要Hilt或多Activity,利用Compose Navigation的嵌套导航图作用域就能实现表单ViewModel在返回HomePage时自动销毁的需求,这是2023年版本的原生最佳实践。
实现步骤
1. 定义导航路由与嵌套图
先统一管理所有页面路由,并为表单流程单独定义一个嵌套导航图的根路由:
sealed class Screen(val route: String) { object Home : Screen("home") object FormPage1 : Screen("form_page_1") object FormPage2 : Screen("form_page_2") object FormPage3 : Screen("form_page_3") // 表单流程嵌套图的根路由 companion object { const val FORM_FLOW_ROOT = "form_flow_root" } }
2. 配置NavHost与嵌套导航图
在MainActivity的NavHost中,将三个表单页面纳入同一个嵌套导航图,并指定该嵌套图的起始页为FormPage1:
NavHost(navController = navController, startDestination = Screen.Home.route) { // 首页 composable(Screen.Home.route) { HomePage( onCreateFormClick = { // 导航到表单嵌套图的起始页 navController.navigate(Screen.FORM_FLOW_ROOT) } ) } // 表单流程嵌套导航图 navigation( startDestination = Screen.FormPage1.route, route = Screen.FORM_FLOW_ROOT ) { // 表单第一页 composable(Screen.FormPage1.route) { backStackEntry -> // 绑定到嵌套图的根作用域获取ViewModel val formViewModel = viewModel<FormPageViewModel>( viewModelStoreOwner = backStackEntry ) FormPage1( viewModel = formViewModel, onNext = { navController.navigate(Screen.FormPage2.route) } ) } // 表单第二页 composable(Screen.FormPage2.route) { backStackEntry -> // 通过parent获取嵌套图的根作用域,共享同一个ViewModel val formViewModel = viewModel<FormPageViewModel>( viewModelStoreOwner = backStackEntry.parent!! ) FormPage2( viewModel = formViewModel, onNext = { navController.navigate(Screen.FormPage3.route) }, onBack = { navController.popBackStack() } ) } // 表单第三页 composable(Screen.FormPage3.route) { backStackEntry -> val formViewModel = viewModel<FormPageViewModel>( viewModelStoreOwner = backStackEntry.parent!! ) FormPage3( viewModel = formViewModel, onSubmit = { // 提交后直接返回首页,清空整个表单流程的回退栈 navController.popBackStack(Screen.Home.route, inclusive = false) }, onBack = { navController.popBackStack() } ) } } }
3. 核心原理说明
- ViewModel作用域绑定:嵌套导航图的根BackStackEntry会作为整个表单流程的ViewModelStoreOwner。FormPage1是嵌套图的起始页,其自身的BackStackEntry就是根作用域;FormPage2、FormPage3通过
backStackEntry.parent!!获取根作用域,从而实现三个页面共享同一个ViewModel。 - 自动销毁时机:当从表单页面返回HomePage时(无论是从FormPage1直接返回,还是从FormPage3提交后返回),整个嵌套导航图的所有BackStackEntry都会被弹出。此时根作用域的ViewModelStore被清理,FormPageViewModel会自动销毁,下次进入表单流程时会重新实例化。
- 流程内数据保留:在表单流程内部跳转(比如FormPage2回到FormPage1)时,嵌套图的根作用域依然存在,ViewModel不会被销毁,符合表单数据共享的需求。
关键注意事项
- 返回HomePage时,必须使用
popBackStack(Screen.Home.route, inclusive = false),确保一次性弹出表单流程的所有页面,而不是仅弹出当前页面。 - 无需手动调用ViewModel的清除方法,Navigation组件会自动管理ViewModel的生命周期,完全符合原生最佳实践。
内容的提问来源于stack exchange,提问作者William Humphreys
相关产品推荐
相关产品推荐

