navigationandroid-jetpack-compose

Retrieve parameters in navigation graph scoped viewmodel in Jetpack Compose


Very similar to this issue but with a slight difference: Scoping States in Jetpack Compose

I have a scoped viewModel in a navigation graph. I can retrieve it but I do not find an elegant way of retrieving parameters ...

NavHost(navController, ...) {
    ...
    composable(routeWithParameter) {
       // (A): here, I need an 'args' parameter from route. 
       // My current solution is to 'add again' the parameter:
       val args = it.arguments?.getString("argumentKey") // retrieve 'args' from route

       val state = it.getRememberedParent(navController) // get parent
       
       state.arguments?.putString("argumentKey", args ) // add again the 'args' in parent state
       
       val viewModel = hiltViewModel<TViewModel>(state)
       Log.d("MISC", "id =${System.identityHashCode(viewModel)}") // same instance as (B)
       ...
    }
    composable(route) {
       // (B):      
       val viewModel = hiltViewModel<TViewModel>(it.getRememberedParent(navController))
       Log.d("MISC", "id =${System.identityHashCode(viewModel)}") //same instance as (A)
       ...
    }
    ...
}


@Composable
fun NavBackStackEntry.getRememberedParent(navController: NavHostController): NavBackStackEntry {
    val parentId = this.destination.parent!!.id
    return remember(this) { navController.getBackStackEntry(parentId) }
}

In the view model, parameters are retrieved using SavedStateHandle like this:


@HiltViewModel
class EditFarmViewModel @Inject constructor(
    state: SavedStateHandle,
) : ViewModel() {

 private val _args = checkNotNull(state.get<String>("argumentKey"))

In order to get the same viewModel instance in (A) and (B), I cannot build viewModel (A) by simply using hiltViewModel() without state as it leads to another instance beeing created for (B)...

I haven't found any trick in the doc (https://developer.android.com/jetpack/compose/libraries#hilt)

=> How to retrieve route parameters for (A) without re-inserting them?

OR

=> How to get the same scoped instance without using the getBackStackEntry to find parent in (A) (and so, use the initial route parameters)?


Solution

  • In fact, the error was I had to create a nested graph to access shared scope viewmodel composable and give the navigation parameter to the nested graph root.

    like this:

    NavHost(navController, ...) {
         // => NESTED Navigation graph HERE that is the only one with arguments
         navigation(route = routeWithParameters, startDestination = nestedrouteA
         ){
              composable(nestedrouteA) {
                val state = it.getRememberedParent(navController) // get parent
                val viewModel = hiltViewModel<TViewModel>(state)
                Log.d("MISC", "id =${System.identityHashCode(viewModel)}") // same instance as (B)
                ...
             }
             composable(nestedrouteB) {
                // (B):      
                val viewModel = hiltViewModel<TViewModel>(it.getRememberedParent(navController))
                Log.d("MISC", "id =${System.identityHashCode(viewModel)}") //same instance as (A)
                ...
             }
         }
         ...
    }