clm*_*mno 7 android viewmodel android-jetpack-compose dagger-hilt
我想在许多可组合项之间共享视图模型。就像我们如何在活动中的片段之间共享视图模型一样。
但是当我尝试这个时
setContent {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
navigation(startDestination = "username", route = "login") {
// FIXME: I get an error here
val viewModel: LoginViewModel = viewModel()
composable("username") { ... }
composable("password") { ... }
composable("registration") { ... }
}
}
}
Run Code Online (Sandbox Code Playgroud)
我收到一个错误
@Composable 调用只能在 @Composable 函数的上下文中发生
需要
Philip Dukhov对问题的回答:如何在 Compose NavGraph 内的两个或多个 Jetpack 可组合项之间共享视图模型?
但在这种方法中,视图模型保留在启动它的活动的范围内,因此永远不会被垃圾收集。
(从文档复制)
导航返回堆栈不仅为每个单独的目的地存储NavBackStackEntry ,还为包含单独目的地的每个父导航图存储 NavBackStackEntry。这允许您检索范围
NavBackStackEntry为导航图的 。导航图范围NavBackStackEntry提供了一种创建ViewModel导航图范围的方法,使您能够在图的目标之间共享 UI 相关数据。ViewModel以这种方式创建的任何对象都会一直存在,直到关联对象NavHost及其ViewModelStore被清除或从返回堆栈中弹出导航图为止。
这意味着我们可以使用 NavBackStackEntry 来获取我们所在的导航图的范围,并使用它来ViewModelStoreOwner获取该范围的视图模型。
将其添加到每个可组合项中以获取BackStackEntryfor login,然后使用它作为ViewModelStoreOwner来获取视图模型。
val loginBackStackEntry = remember { navController.getBackStackEntry("login") }
val loginViewModel: LoginViewModel = viewModel(loginBackStackEntry)
Run Code Online (Sandbox Code Playgroud)
所以最终代码改为
setContent {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
navigation(startDestination = "username", route = "login") {
composable("username") {
val loginBackStackEntry = remember { navController.getBackStackEntry("login") }
val loginViewModel: LoginViewModel = viewModel(loginBackStackEntry)
...
}
composable("password") {
val loginBackStackEntry = remember { navController.getBackStackEntry("login") }
val loginViewModel: LoginViewModel = viewModel(loginBackStackEntry)
...
}
composable("registration") {
val loginBackStackEntry = remember { navController.getBackStackEntry("login") }
val loginViewModel: LoginViewModel = viewModel(loginBackStackEntry)
...
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
复制自ianhanniballake答案
这也可以使用扩展来实现
@Composable
fun <reified VM : ViewModel> NavBackStackEntry.parentViewModel(
navController: NavController
): VM {
// First, get the parent of the current destination
// This always exists since every destination in your graph has a parent
val parentId = destination.parent!!.id
// Now get the NavBackStackEntry associated with the parent
val parentBackStackEntry = navController.getBackStackEntry(parentId)
// And since we can't use viewModel(), we use ViewModelProvider directly
// to get the ViewModel instance, using the lifecycle-viewmodel-ktx extension
return ViewModelProvider(parentBackStackEntry).get()
}
Run Code Online (Sandbox Code Playgroud)
navigate(secondNestedRoute, startDestination = nestedStartRoute) {
composable(route) {
val loginViewModel: LoginViewModel = it.parentViewModel(navController)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5956 次 |
| 最近记录: |