Dagger/Hilt ViewModel Injection (with compose and navigation-compose)
See original GitHub issueHello,
I am currently trying to build an app with only Compose (meaning no Fragments and navigation-compose, along with architecture components such as Hilt and ViewModel).
I tried using the viewModel function with the defaultViewModelProviderFactory
of the Activity.
java.lang.IllegalArgumentException: SavedStateProvider with the given key is already registered
at androidx.savedstate.SavedStateRegistry.registerSavedStateProvider(SavedStateRegistry.java:111)
at androidx.lifecycle.SavedStateHandleController.attachToLifecycle(SavedStateHandleController.java:50)
at androidx.lifecycle.SavedStateHandleController.create(SavedStateHandleController.java:70)
at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:67)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at androidx.compose.ui.viewinterop.ViewModelKt.get(ViewModel.kt:75)
at androidx.compose.ui.viewinterop.ViewModelKt.viewModel(ViewModel.kt:60)
I had to move this code inside a NavHost
Composable. I reported this on the KotlinLang Slack and was told this issue relates to #2152 . It uses the incorrect Scope for a Navigation Composable.
In the case of the related issue, the scope is too small and in my case, it is the exact opposite Problem.
In that issue I linked, the fragment is within the Navigation graph, so the issue is that the saved state is too small of a scope (the navigation graph encompasses multiple fragments). In your Compose case, the entire navigation graph in within the single Activity/Fragment, so there the scope is too large and you end up saving state multiple times with the same key.
Although the issue should be fixed by a more correct approach to scoping, it is still worth to file a bug for the inverse problem with saved state.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:4
- Comments:19 (5 by maintainers)
Top GitHub Comments
The
hilt-navigation
version1.0.0-alpha03
is great. ViewModels are retained via theNavBackStackEntry
now. Love it.There’s still a limitation with building a “compose only” navigation system though. I will try to explain with an example.
Suppose you have a bottom nav with 3 items. You could easily implement that with something like this:
Navigating using the back button works great now (Data is persisted) since the ViewModel gets its
ViewModelStoreOwner
from theNavBackStackEntry
. Horray.The problem
But if you click on any of the entries a second time, the screen stacks. A new viewmodel is created since a new
NavBackStackEntry
is created.Solution?
We need something to prevent the stacking of back stack entries. If an item is re-selected, it should be removed from its position in the stack and placed at the top.
launchSingleTop
is great but only works if the entry is already at the top of the stack.Thanks and sorry for the long post I probably should have created a feature request. One might already exists?
@Guimareshh, we have a scheduled release of the
androidx.hilt
artifacts on January 27th (if all goes well), sorry for the trouble. As the reference comment shows, there will be ahilt-navigation
artifact you can use to get retrieve aViewModel
out of aNavBackStackEntry
You don’t have to switch to Hilt to make DI work with Compose, but Hilt still makes some things easier, such as App, Activity and Service injections along with ViewModel injection. Note that the functions in
hilt-navigation
scheduled for release will also let you usenavigation-compose
ViewModels which have more granular scopes.