I am using the Navigation Component of Android Jetpack (2.2.0-alpha01).
I wish to use a child NavHostFragment nested inside my main NavHostFragment, equipped with its own child nav graph. Please view the following image for context:
The child nav host is defined like this inside the fragment that is at the front of the MainNavHost's stack:
<fragment
android:id="@+id/childNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="false"
app:navGraph="@navigation/child_graph" />
Inside the fragment that is at the front of the CHILD Nav Host Fragment, I am trying to get a ViewModel scoped to the R.navigation.child_graph by using the following code:
private val childGraphScopedViewModel: ChildGraphScopedViewModel by navGraphViewModels(R.navigation.child_graph) {
viewModelFactory
}
When accessing the childGraphScopedViewModel, I am getting a crash with the error message:
java.lang.IllegalArgumentException: No NavGraph with ID 2131689472 is on the NavController's back stack.
I believe the lazy init call by navGraphViewModel()
is looking for the navgraph inside the mainGraph.
How can I access a child navHostFragment scoped ViewModel? Thank you for your time.
navGraphViewModels is a kotlin extension function for fragments that creates nav graph scoped view models. It accepts two parameters: a nav graph ID and also a viewmodel factory. You can simply write: Val order selection by navGraphViewModels(R. id.
NavController manages app navigation within a NavHost . Apps will generally obtain a controller directly from a host, or by using one of the utility methods on the Navigation class rather than create a controller directly. Navigation flows and destinations are determined by the navigation graph owned by the controller.
To add the NavHostFragment, you have to go to your . xml file of your activity and add the following. The app:defaultNavHost=”true” is simply stating that you want this to be the NavHost that intercepts and works as the back button on your device.
You can do that by providing the viewModelStore of child NavController
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?
) {
super.onViewCreated(view, savedInstanceState)
val childHostFragment = childFragmentManager
.findFragmentById(R.id.childNavHostFragment) as NavHostFragment
val childNavController = childHostFragment.navController
val childViewModel: ChildGraphScopedViewModel = ViewModelProvider(
childNavController.getViewModelStoreOwner(R.navigation.child_graph)
).get(ChildGraphScopedViewModel::class.java)
}
I wrote a Kotlin Extension for making it easier
inline fun <reified T: ViewModel> NavController.viewModel(@NavigationRes navGraphId: Int): T {
val storeOwner = getViewModelStoreOwner(navGraphId)
return ViewModelProvider(storeOwner)[T::class.java]
}
Usage
val viewModel = findNavController().viewModel(R.navigation.nav)
This works for me without defining aby custom ViewModelProvider
(2.2.0):
private val viewModel: ChildGraphScopedViewModel by navGraphViewModels(R.id. child_graph)
An Easy mistake to make is to use R.navigation.child_graph
(bad) instead of R.id.child_graph
(good)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With