Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to share a viewmodel between two or more Jetpack composables inside a Compose NavGraph?

Consider this example.

For authentication, we'll be using 2 screens - one screen to enter phone number and the other to enter OTP.

Both these screens were made in Jetpack Compose and the for the NavGraph, we are using compose navigation.

Also I have to mention that DI is being handled by 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>())
        }
    }
}

So how can we share the same viewmodel among two or more composables in a Jetpack compose NavGraph?

like image 272
Gideon Paul Avatar asked Sep 05 '25 23:09

Gideon Paul


2 Answers

You can to pass your top viewModelStoreOwner to each destination

  1. directly passing to .viewModel() call, composable("first") in my example
  2. overriding LocalViewModelStoreOwner for the whole content, so each composable inside CompositionLocalProvider will have access to the same view models, composable("second") in my example
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()
        }
    }
}

In the second case, you can get your model at any level of the composition tree, which is inside the CompositionLocalProvider:

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

@Composable
fun SomeView() {
    val model = viewModel<SharedModel>()
}
like image 110
Philip Dukhov Avatar answered Sep 08 '25 12:09

Philip Dukhov


Using Hilt you could do something like the below. But since you are using Koin I don't know the way of Koin yet.

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

Official doc: https://developer.android.com/jetpack/compose/libraries#hilt

like image 28
Rafiul Avatar answered Sep 08 '25 11:09

Rafiul