I’d like to handle a <navigation result> in a `Vie...
# compose
j
I’d like to handle a navigation result in a
ViewModel
. I expected that I could use the
SavedStateHandle
injected in my
ViewModel
to listen to the navigation result, but I see that this
SavedStateHandle
is not the same as the one from the
NavBackStackEntry
that holds the result. Details in thread 🧵
I expected that the
savedStateHandle
in my `ViewModel`:
Copy code
@HiltViewModel
class MyViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
)
was the same as the
savedStateHandle
from the
NavBackStackEntry
that was used as the
ViewModelStoreOwner
to create the `ViewModel`:
Copy code
composable("route") {
    val viewModel: MyViewModel = hiltViewModel(
        remember { navController.getBackStackEntry("route") }
    )
    val savedStateHandle = navController.getBackStackEntry("route").savedStateHandle
    MyComposable(/* args */)
}
… but it’s not. Is there a way to accomplish this without having to update properties on the
ViewModel
after it’s been created?
i
Every ViewModel gets its own SavedStateHandle - if you created two separate ViewModels associated with the same NavBackStackEntry, they'd have separate SavedStateHandles
All
navBackStackEntry.savedStateHandle
does is create its own ViewModel and get its SavedStateHandle: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt;l=117 so yeah, it'll be a different SavedStateHandle
👀 1
You'd need to request exactly the same ViewModel (i.e., ask for a ChargeSessionsViewModel) if you want that exact ViewModel. That's certainly supported and means you don't need to go through SavedStateHandle at all
But at that point, maybe you should take a step back and think about if your initial request makes sense at all. If you're on your second screen and you go through process death and recreation, then that previous destinations ViewModel won't exist anymore - it'll be recreated when you access it. It will have its SavedStateHandle restored and maybe that's enough for your case?
But it really sounds like if you aren't processing your results in the destination itself, then maybe you shouldn't be returning a result at all and should instead have that second destination directly access what shared repository later that both ViewModels use as their single source of truth
j
You’re right, hoisting this event to a shared repository might be the best solution here.
I still think that the mechanism for communicating simple data between destinations is useful, but you end up having to handle that data in the UI. It would be nice to be able to handle that communication in the ViewModel.
For completeness, this is how I tried to work around that. It works, but it’s not very elegant:
Copy code
composable("route") {
    val viewModel: MyViewModel = hiltViewModel()
    LaunchedEffect(viewModel, navController) {
        val savedStateHandle =
            navController.getBackStackEntry("route").savedStateHandle
        savedStateHandle
            .getLiveData<String?>("result")
            .asFlow()
            .filterNotNull()
            .collect {
                viewModel.onResult(it)
                savedStateHandle["result"] = null
            }
    }
    MyComposable(/* args */)
}