21

I have found a similar question here. At the time of writing this question there is only this answer avaliable, which does not provide any help to me, and I believe also to the person who asked the question.

I checked the repo which is linked in the answer and it "solves" the problem by creating an init method in the viewmodel and calling it in the Activity/Fragment.

Since the viewmodel has already been injected, this solution does not seem like ideal to me, and I would like to know if there are other options available when using hilt.

HelloThere
  • 210
  • 1
  • 3
  • 7
  • 1
    Keep an eye on this [github issue](https://github.com/google/dagger/issues/2287) that tracks this feature request. Here's a workaround that is in alpha I think https://twitter.com/manuelvicnt/status/1273699171078475780 https://gist.github.com/manuelvicnt/437668cda3a891d347e134b1de29aee1 You basically have to use @AssistedInject to pass in runtime arguments to the viewmodel – denvercoder9 May 01 '21 at 23:25

2 Answers2

62

As per this comment and the release of AndroidX Hilt 1.0.0-alpha03, Hilt has supported ViewModels that take a SavedStateHandle as a parameter (right alongside your other injected parameters).

This SavedStateHandle is automatically, without you doing anything, populated with the arguments passed to your fragment (i.e., the same arguments you get from requireArguments() and the same arguments that are read by Safe Args).

Therefore in your ViewModel's constructor, you can immediately access those arguments from the SavedStateHandle, without having to do any manual passing of arguments to your ViewModel.

@HiltViewModel
class MainViewModel @Inject constructor(
    val userDataManager: UserDataManager,
    savedStateHandle: SavedStateHandle
) : ViewModel() {
    init {
        // Use the same argName as in your navigation graph
        val yourArgument: String = savedStateHandle["argName"]
        // Now use that argument to load your data, etc.
    }
}

The feature request for Safe Args integration with SavedStateHandle is already fixed and will be part of the upcoming Navigation 2.4.0-alpha01 release. Once that is released, you'd be able to do something like MainFragmentArgs.fromSavedStateHandle(savedStateHandle) to get the same Args class you're currently able to get from by navArgs() within your ViewModel.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • should'nt "yourArgument" be nullable? or should we just use "savedStateHandle["argName"]!!" – mrzbn Jun 11 '21 at 22:23
  • 13
    You should take over writing the Android Developer Documentation! This should have been mentioned in the docs! Thanks! :) – HumbleBee Jul 10 '21 at 06:51
  • This function is not available to me. MainFragmentArgs.fromSavedStateHandle(savedStateHandle) using the 2.4.0 Nav and the 2.35.1 com.google.dagger:hilt-android. I see there just a hilt 1.0.0 now did they migrate some of/all of this there? – JPM Aug 11 '21 at 21:59
  • @JPM - what version of Android Studio are you using? You have to be using Arctic Fox Canary 10 or higher. – ianhanniballake Aug 11 '21 at 22:53
  • 2
    I tried this but the savedStateHandle is empty, there is no arguments there – Hussien Fahmy Sep 02 '21 at 23:12
  • 1
    Had to read a few more posts before knowing how to use this - with the codes above, from the fragment the ViewModel is set up using `val viewModel: MainViewModel by viewModels()` – Ryan W Nov 25 '21 at 11:41
  • @ianhanniballake What about Hilt viewModel with SavedStateHandle in jetpack compose. What will be the solution? – Guru Karthi R Dec 11 '21 at 21:44
  • 1
    @GuruKarthiR - the [`hiltViewModel()` method](https://developer.android.com/jetpack/compose/libraries#hilt-navigation) already supports `SavedStateHandle`. – ianhanniballake Dec 11 '21 at 21:46
  • Don't forget to update versions of the navigation libraries that are in the top level gradle file and module's gradle file to the same value! https://stackoverflow.com/a/68331336 – James Bond Dec 19 '21 at 11:41
  • @ianhanniballake this works only when viewModel is initialized at first. But what if I want to get the arguments after initialized at any point of time? – Jeevan Rupacha Jun 12 '22 at 16:02
  • @JeevanRupacha - you can, of course, make either the `SavedStateHandle` instance or the `Args` class Navigation can generate for you into a `val` in your ViewModel and access it at any time. – ianhanniballake Jun 12 '22 at 18:09
  • 1
    are there any examples how to use this new api? should we inject the Args instance directly into viewModel constructor? should we convert state handle into the Args to get type safe params? – deviant Jul 09 '22 at 11:01
0

For anyone facing the same challenge and using Hilt, the trick is to initialize the SavedStateHandle in the viewModel constructor i.e.

savedStateHandle: SavedStateHandle = SaveStateHandle()

Then you can access the passed argument like: val id: String? = savedStateHandle["id"] or val id = savedStateHandle.get<String?>("id")