Как поделиться моделью просмотра между двумя или более компонентами Jetpack внутри Compose NavGraph?

Рассмотрим этот пример.

Для аутентификации мы будем использовать 2 экрана: один для ввода номера телефона, а другой для ввода OTP.

Оба эти экрана были созданы в Jetpack Compose и для NavGraph, мы используем навигацию Compose.

Также я должен упомянуть, что DI занимается Коин.

      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>())
        }
    }
}

Итак, как мы можем использовать одну и ту же модель просмотра для двух или более компонентов в Jetpack compose NavGraph?

4 ответа

Вы можете пройти свой топ 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<Model>(viewModelStoreOwner = viewModelStoreOwner)
    }
    composable("second") {
        CompositionLocalProvider(
            LocalViewModelStoreOwner provides viewModelStoreOwner
        ) {
            val model = viewModel<Model>()
        }
    }
}

Используя Hilt, вы можете сделать что-то вроде следующего. Но поскольку вы используете Koin, я еще не знаю, как это сделать.

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

Самый простой и эффективный способ справиться с этим

      @Composable
inline fun <reified T : ViewModel> NavBackStackEntry.sharedViewModel(
    navController: NavController
): T {
    val navGraphRoute = destination.parent?.route ?: return ViewModel()
    val parentEntry = remember(this) {
        navController.getBackStackEntry(navGraphRoute)
    }
    return ViewModel(parentEntry)
}

Затем вызовите его из composefun

      composable("route") {
            it.sharedViewModel<ThoughtViewModel>(navController = navController).apply {
               RequestsScreen(this@apply)
            }
           
        }

Вот еще один способ с Koin.

Он строго делает то же самое, что и проверенный ответ, но его проще написать. Он будет иметь точно такой же viewModelStoreOwner без необходимости писать его явно. Пожалуйста, скажите мне, если я ошибаюсь.

      val navController = rememberNavController()

val sharedViewModel = getViewModel()

NavHost(navController = navController, startDestination = "first") {
    composable("first") {
        // You can use sharedViewModel
    }
    composable("second") {
        // You can use sharedViewModel
    }
}
Другие вопросы по тегам