How do I get an activity-scoped ViewModel in Compo...
# compose
f
How do I get an activity-scoped ViewModel in Compose? Do just call (
hilt
)`viewModel()` in the activity and pass it to my Composable screen?
t
Just call
hiltViewModel()
directly in the composable parameter for
ViewModel
f
wait, doesn't that scope the viewmodel to the composable?
t
No, for now it’s to the activity. If you want you can scope by navigation passing the parameter for it.
c
I think if you use navigation and hilt then when you call hiltViewModel() it'll be scoped to composable, but if you need to scope to just the activity, you need to pass an argument into hiltViewModel(some arg I don't know off the top of my head)
f
I think you just need to instantiate it in the activity
i
hiltViewModel()
/
viewModel()
use
LocalViewModelStoreOwner.current
- when you're in an Activity/Fragment, that'll be the default. When you're within a
NavHost
, it'll be that current destination. You'll just need to be explicit if you want something other than the default
Copy code
val outerOwner = LocalViewModelStoreOwner.current
NavHost(...) {
  composable(...) {
    val outerScopedViewModel: MyViewModel = viewModel(outerOwner)
  }
}
Of course, if you want something scoped to every destination in a
NavHost
, that's exactly why you can define a route for your root navigation graph (the one that is always present):
Copy code
NavHost(navController, startDestination = "home", route="root") {
  composable(...) {
    val rootScopedViewModel: MyViewModel = viewModel(navController.getBackStackEntry("root"))
  }
}
f
@Ian Lake if I want an activity-scoped ViewModel in my composable screen, do I instantiate it in the activity (with
hiltViewModel()
) and then pass it down through to that Composable?
1
so in
onCreate
i
There's no reason to do that. All you need is the right owner, literally nothing else matters as the owner sets all of the scoping
c
Makes sense. Yeah, I think it's just a shift from how I currently think about VMs. My old apps were single activity with nav and fragments. So all I know is: 1. fragment/screen scope. 2. nav graph scope. 3. and activity scope. so I think it's just learning to break away from that mindset.
i
Those are indeed the three main types of owners, nothing has changed there
c
Hm. Okay. then I'm a little lost. This is how I would get an activityVM in my fragments in the past.
Copy code
private val sharedViewModel: ActivityViewModel by activityViewModels()
How would I do this in the compose world since all of my destinations/screens are in my NavHost?
i
Did you look at what
activityViewModels()
is doing? https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:fragment[…]main/java/androidx/fragment/app/FragmentViewModelLazy.kt;l=80 It is just getting the activity and using its
ViewModelStore
, just like any other
ViewModelStoreOwner
Instead of reaching up, Compose wants you to pass down the right values - hence the
outerOwner
approach I mentioned above
c
I did not. outerOwner example above makes sense now. Thank you!
f
@Ian Lake but how do I get the activity to pass it as the owner in the composable?
wouldnt I need to pass the activity down to the composable?
oh I guess the outerOwner approach above is the answer
i
Yep. It might be the activity or a fragment - whatever is hosting your whole Composable hierarchy
💯 1
f
alright, thank you!
@Ian Lake how do I handle nullability of LocalViewModelStoreOwner.current 🤔
I don't get why I wouldn't just instantiate the ViewModel in the place where you call LocalViewModelStoreOwner.current and then pass that ViewModel to the screens
then I don't have to handle nullability
i
If you're in an Activity/Fragment, you can use `!!`; it'll always be filled in there. Like I said, it doesn't matter at all where you call
viewModel()
- what matters is using the right owner.
👍 1
f
but if I call viewModel() in the activity the owner will be set to that activity, right?
seems pretty straight forward and no
!!
wait, I am referring to
hiltViewModel()
maybe only
hiltViewModel()
does that automatic scoping
i
hiltViewModel()
just calls
viewModel()
with a Hilt provided factory. They both internally do the
!!
for you and throw if it isn't there (which, like I said, it will always be there in an activity/fragment)
👍 1
f
ok, thank you