如何在NavGraph组件之间共享视图模型(仅)



我想在许多可组合物之间共享一个视图模型。就像我们如何在Activity中的片段之间共享视图模型一样。

但是当我尝试这个

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") { ... }
}
}
}

我得到一个错误

@Composable调用只能在@Composable函数的上下文中发生

  • 视图模型应该只在NavGraph作用域中是活动的。
  • 当我去一个不同的路线,然后回来我应该初始化一个新的视图模型(这就是为什么我在NavGraph中调用它)

几乎相同的解

  1. 回答Philip Dukhov的问题如何在两个或多个Jetpack可组合件之间共享视图模型在撰写NavGraph?

    但是在这种方法中,视图模型停留在启动它的活动的范围内,因此永远不会被垃圾收集。

解决方案1(从文档中复制)

导航返回堆栈不仅为每个单独的目的地存储NavBackStackEntry,而且为包含单个目的地的每个父导航图存储NavBackStackEntry。这允许您检索作用域为导航图的NavBackStackEntry。导航图作用域的NavBackStackEntry提供了一种方法来创建作用域为导航图的ViewModel,使您能够在图的目的地之间共享与ui相关的数据。以这种方式创建的任何ViewModel对象一直存在,直到相关的NavHost和它的ViewModelStore被清除,或者直到导航图从后堆栈弹出。

这意味着我们可以使用NavBackStackEntry来获取我们所在的导航图的范围,并使用它作为ViewModelStoreOwner来获取该范围的视图模型。

将此添加到每个可组合项中以获得loginBackStackEntry,然后将其用作ViewModelStoreOwner以获得视图模型。

val loginBackStackEntry = remember { navController.getBackStackEntry("login") }
val loginViewModel: LoginViewModel = viewModel(loginBackStackEntry)
所以最后的代码变成了
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)
... 
}
}
}
}

解决方案2

摘自ianhanniballake答案

也可以使用扩展名

来实现
  1. 获取当前作用域并获取或创建该作用域的视图模型
@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()
}
  1. 然后简单地在你的导航图
  2. 中使用这个扩展
navigate(secondNestedRoute, startDestination = nestedStartRoute) {
composable(route) {
val loginViewModel: LoginViewModel = it.parentViewModel(navController)
}
}

首先,创建一个函数来获取导航ViewModelStoreOwner

@Composable
fun rememberParentViewModelStoreOwner(
navController: NavHostController,
parentRoute: String,
): ViewModelStoreOwner {
return remember(navController.currentBackStackEntry) {
object : ViewModelStoreOwner {
override val viewModelStore =
navController.getBackStackEntry(parentRoute).viewModelStore
}
}
}

然后,检索它并使用它在所需的可组合构建器中构造ViewModel

composable("username") {
val loginViewModelStoreOwner = rememberParentViewModelStoreOwner(navController, "login")
val loginViewModel: LoginViewModel = viewModel(loginViewModelStoreOwner)
// ...
}
composable("password") {
val loginViewModelStoreOwner = rememberParentViewModelStoreOwner(navController, "login")
val loginViewModel: LoginViewModel = viewModel(loginViewModelStoreOwner)
// ...
}
composable("registration") {
val loginViewModelStoreOwner = rememberParentViewModelStoreOwner(navController, "login")
val loginViewModel: LoginViewModel = viewModel(loginViewModelStoreOwner)
// ...
}

解释:

通常,ViewModel是用可组合构建器的NavBackStackEntry中的局部作用域ViewModelStoreOwner(LocalViewModelStoreOwner.current)构造的。如果已经用相同的ViewModelStoreOwner构造了ViewModel,它将重用前一个实例,而不是再次创建它。在本例中,我们将使用登录导航ViewModelStoreOwner来构造ViewModel

相关内容

  • 没有找到相关文章

最新更新