Как поделиться моделью просмотра между двумя или более компонентами 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
к каждому пункту назначения
- непосредственно переходя к
.viewModel()
вызов,composable("first")
в моем примере - преобладающий
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
}
}