如何在 Compose NavGraph 内的两个或多个 Jetpack 可组合项之间共享视图模型?

Gid*_*aul 23 kotlin koin android-jetpack-compose jetpack-compose-navigation

考虑这个例子。

对于身份验证,我们将使用 2 个屏幕 - 一个屏幕用于输入电话号码,另一个屏幕用于输入 OTP。

这两个屏幕都是用 Jetpack Compose 制作的,对于 NavGraph,我们使用组合导航。

另外我必须提到 DI 是由 Koin 处理的。

val navController = rememberNavController()

NavHost(navController) {
    navigation(
        startDestination = "phone_number_screen",
        route = "auth"
    ) {
        composable(route = "phone_number_screen") {
            // Get's a new instance of AuthViewModel
            PhoneNumberScreen(viewModel = getViewModel<AuthViewModel>())
        }

        composable(route = "otp_screen") {
            // Get's a new instance of AuthViewModel
            OTPScreen(viewModel = getViewModel<AuthViewModel>())
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

那么,我们如何在 Jetpack 组合 NavGraph 中的两个或多个可组合项之间共享相同的视图模型呢?

Phi*_*hov 26

您可以将您的上衣传递viewModelStoreOwner到每个目的地

  1. 在我的示例中直接传递给.viewModel()调用composable("first")
  2. 覆盖LocalViewModelStoreOwner整个内容,因此在我的示例中CompositionLocalProvider,内部的每个可组合项都可以访问相同的视图模型composable("second")
val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
    "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
}
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "first") {
    composable("first") {
        val model = viewModel<SharedModel>(viewModelStoreOwner = viewModelStoreOwner)
    }
    composable("second") {
        CompositionLocalProvider(
            LocalViewModelStoreOwner provides viewModelStoreOwner
        ) {
            SecondScreen()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,您可以在组合树的任何级别获取模型,该模型位于CompositionLocalProvider

@Composable
fun SecondScreen() {
    val model = viewModel<SharedModel>()
    SomeView()
}

@Composable
fun SomeView() {
    val model = viewModel<SharedModel>()
}
Run Code Online (Sandbox Code Playgroud)


Raf*_*iul 13

使用 Hilt 你可以做如下的事情。不过既然你用的是Koin,我还不知道Koin的使用方式。

@Composable
fun MyApp() {
    NavHost(navController, startDestination = startRoute) {
        navigation(startDestination = innerStartRoute, route = "Parent") {
            // ...
            composable("exampleWithRoute") { backStackEntry ->
                val parentEntry = remember {
                  navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(
                  parentEntry
                )
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

官方文档: https: //developer.android.com/jetpack/compose/libraries#hilt

  • 这是谷歌推荐的方法,效果很好。只需考虑到这一点“以这种方式创建的任何 ViewModel 对象都会一直存在,直到关联的 NavHost 及其 ViewModelStore 被清除或直到从返回堆栈中弹出导航图”。 (2认同)