如何仅在Jetpack Compose NavGraph组件间共享ViewModel
报错原因
navigation 构造块本身不属于 @Composable 作用域,仅 composable(路由) { } 内部的代码块才是Composable执行环境,你直接在navigation块内调用viewModel()这个Composable函数,自然会触发@Composable invocations can only happen from the context of a @Composable function编译错误。
解决方案
要实现ViewModel绑定当前NavGraph的生命周期、同导航图下多个Composable共享、离开导航图自动销毁的需求,只需要在每个子页面获取ViewModel时,指定其ViewModelStoreOwner为当前导航图的BackStackEntry即可。
完整示例代码
setContent { val navController = rememberNavController() NavHost(navController = navController, startDestination = "home") { navigation(startDestination = "username", route = "login") { composable("username") { // 获取login导航图对应的BackStackEntry val loginNavEntry = remember { navController.getBackStackEntry("login") } // 绑定导航图的StoreOwner获取ViewModel val viewModel: LoginViewModel = viewModel(viewModelStoreOwner = loginNavEntry) // 页面业务逻辑 } composable("password") { val loginNavEntry = remember { navController.getBackStackEntry("login") } val viewModel: LoginViewModel = viewModel(viewModelStoreOwner = loginNavEntry) // 页面业务逻辑 } composable("registration") { val loginNavEntry = remember { navController.getBackStackEntry("login") } val viewModel: LoginViewModel = viewModel(viewModelStoreOwner = loginNavEntry) // 页面业务逻辑 } } // 其他路由定义 } }
简化写法
如果觉得重复代码太多,可以封装成通用的Composable扩展函数:
@Composable fun NavBackStackEntry.getScopedViewModel<T: ViewModel>( navController: NavController, graphRoute: String ): T { val graphEntry = remember { navController.getBackStackEntry(graphRoute) } return viewModel(viewModelStoreOwner = graphEntry) }
在子页面中直接调用即可:
composable("username") { entry -> val viewModel = entry.getScopedViewModel<LoginViewModel>(navController, "login") }
Hilt用户适配
如果你使用Hilt注入ViewModel,只需把viewModel()替换为hiltViewModel()即可,参数逻辑完全一致:
val viewModel: LoginViewModel = hiltViewModel(loginNavEntry)
生命周期符合说明
- 同导航图下所有子页面获取到的都是同一个ViewModel实例,可共享状态
- 当你离开login导航图(回退到home或跳转到其他非login子路由)时,login导航图对应的BackStackEntry会被系统销毁,绑定在其上的ViewModel也会同步被回收,不会内存泄漏
- 下次重新进入login导航图时会生成新的BackStackEntry,自动初始化全新的ViewModel实例,完全匹配你的需求。
内容的提问来源于stack exchange,提问作者clamentjohn
相关产品推荐
相关产品推荐

