Q: <How to share a viewmodel between NavGraph comp...
# compose
c
Q: How to share a viewmodel between NavGraph components (only) I would like to share a viewmodel between many composables. Just like how we share a viewmodel between fragments within an Activity. But when I try this
Copy code
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") { ... }
        }
    }
}
I get an error
@Composable invocations can only happen from the context of a @Composable function
Need • The viewmodel should only be active in the NavGraph Scope. • When I go to a different route and come back I should initialize a new viewmodel (this is why I'm calling it in the NavGraph) Almost similar solution 1. Answer by [Philip Dukhov](https://stackoverflow.com/users/3585796/philip-dukhov) for the question [How to share a viewmodel between two or more Jetpack composables inside a Compose NavGraph?](https://stackoverflow.com/a/68857871/5698740) a. But in this approach the viewmodel stays in the scope of the activity that launched it and so is never garbage collected.
c
When I go to a different route and come back I should initialize a new viewmodel (this is why I’m calling it in the NavGraph)
What do you mean by this? Can you give us an example?
c
Consider an Instagram profile in the Instagram app. There are 3-4 fragments with images, reels, videos etc. And one single activity called
Profile
. But when we come to compose. We don't have an activity instead we have nav graphs. So we will add a route
profile
and have a bunch of fragments below it as "sub routes"
c
But in that case, to implement the sub routes, you have a nested
NavHost
es well?
c
This doc seem to point towards my answer but it is not straight forward and the doc is not specific for compose. https://developer.android.com/guide/navigation/navigation-programmatic#share_ui-related_data_between_destinations_with_viewmodel
I do have a nested navigation in the shared code snippet.
c
I see. You can do something like this, but you have to add this inside your three composables:
Copy code
val loginBackStackEntry = remember { navController.getBackStackEntry("login") }
val loginViewModel: LoginViewModel = viewModel(loginBackStackEntry)
c
So for my understanding: 1. We are getting the
BackStackEntry
for the current navGraph
login
2. This backStackEntry, according to the navigation docs is scoped to a navigation graph
login
3. And since we have a scope for the navGraph we can have our
viewmodel
in that scope and share it among all the members of the scope. 4. We are using
remember
because we are getting the scope as a state so that the composable can be recomposed when the scope changes? Am I right?
c
Yes, Except 4. is only for not getting the back stack entry on every recomposition, only on the first one and return the same instance afterwards.
👍 1
i
The
remember
part is particularly important when you consider what is happening when the system back button is pressed: your screen and that whole navigation graph may no longer be on the back stack (since the back button just popped them), but your destination is still on screen and being recomposed (since it is animating out). If you leave off the
remember
, then you've lost your reference to that
NavBackStackEntry
(internally, the NavController knows that the navigation graph is animating out, so it is still valid to keep that reference up until your screen finishes it's animation and is disposed, no problem there)
c
@Ian Lake in your answer to SO question https://stackoverflow.com/questions/64955859 you did not use a
remember
, instead, you are getting the viewmodel directly from the navController. And in your last comments you say
it is still valid to keep that reference up until the screen finishes it's animation
So adding
remember
is just a good measure and not needed? Basically my question is • Why didn't you use remember in your answer
i
You always need to use
remember
. That answer was from before there was any animation support at all
👍 1
c
Could you modify the answer to reflect the most recent version of compose? It is the selected answer and many new comers won't realise they have to use
remember
i
I mean, the entire answer is obsolete since the APIs do exist now on
viewModel()
You don't need any of that code, which is why there are official docs now
FWIW, I updated the answer there to use the newer APIs and
remember
c
Awesome! I'm gonna guess a lot of today's internet's answers are going to have older API versions/implementations. I'm happy I got to ask you this in person