【问题标题】:How to share a viewmodel between NavGraph components (only)如何在 NavGraph 组件之间共享视图模型(仅限)
【发布时间】:2021-10-20 08:08:55
【问题描述】:

我想在许多可组合项之间共享一个视图模型。就像我们在 Activity 中的 Fragment 之间共享视图模型一样。

但是当我尝试这个时

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回答问题How to share a viewmodel between two or more Jetpack composables inside a Compose NavGraph?

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

【问题讨论】:

  • Compose Navigation 基于 Jetpack Navigation,因此本文档中的所有内容都应适用于 Compose。答案是使用 Compose 代码
  • Navigation Compose 的替代解决方案是 Jetmagic。它对共享视图模型有很好的支持:github.com/JohannBlake/Jetmagic
  • @AndroidDev 这个多平台友好吗?否则为什么要使用非 jetpack 库进行 android 开发?我们还使用 Hilt,它是基于 android 构建的,并且具有用于 compose 的互操作和文档
  • @clmno 它是开源的,完全用 Kotlin 编写,所以它应该是多平台友好的。肯定比 Navigation Compose 更胜一筹,因为它甚至可以让您以类似于在旧视图系统下完成 xml 布局的方式创建响应式布局..

标签: android viewmodel android-jetpack-compose dagger-hilt


【解决方案1】:

解决方案 1

(复制自docs

导航返回堆栈不仅为每个单独的目的地存储一个NavBackStackEntry,还为每个包含单独目的地的父导航图存储一个NavBackStackEntry。这允许您检索范围为导航图的NavBackStackEntry。导航图范围的NavBackStackEntry 提供了一种创建ViewModel 的方法,该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答案

这也可以使用extension 来实现

  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. 然后只需在导航图中使用此扩展程序
navigate(secondNestedRoute, startDestination = nestedStartRoute) {
  composable(route) {
    val loginViewModel: LoginViewModel = it.parentViewModel(navController)
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    相关资源
    最近更新 更多