I need to transfer data from one fragment to another. Now the recommended way to do this is to use a shared ViewModel
. To get the same instance available in both fragments, common owner is needed. As it can be their common Activity
. But with this approach (In the case of Single Activity), the ViewModel
instance will live throughout the entire application. In the classic use of fragments, you can specify ViewModelProvider (this)
in the parent fragment, and ViewModelProvider (getParentFramgent ())
in the child. Thus, the scope of ViewModel
is limited to the life of the parent fragment. The problem is that when using Navigation Component, getParentFramgent ()
will return NavHostFragment, not the parent fragment. What do I need to do?
Code samples:
Somewhere in navigation_graph.xml:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav"
app:startDestination="@id/mainMenuFragment">
<fragment
android:id="@+id/mainMenuFragment"
android:name="com.mypackage.mainmenu.MainMenuFragment"
android:label="MainMenu"
tools:layout="@layout/fragment_main_menu">
<action
android:id="@+id/start_game_fragment"
app:destination="@id/gameNav" />
</fragment>
<navigation
android:id="@+id/gameNav"
app:startDestination="@id/gameFragment">
<fragment
android:id="@+id/gameFragment"
android:name="com.mypackage.game.GameFragment"
android:label="@string/app_name"
tools:layout="@layout/fragment_game"/>
</navigation>
</navigation>
Somewhere in MainMenuFragment
:
override fun startGame(gameSession: GameSession) {
//This approach doesn't work
ViewModelProvider(this)[GameSessionViewModel::class.java].setGameSession(
gameSession
)
findNavController().navigate(R.id.start_game_fragment)
}
GameFragment
:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
gameSessionViewModel =
ViewModelProvider(requireParentFragment())[GameSessionViewModel::class.java].apply {
val session = gameSession.value
)
}
}
EDIT:
I can use NavHostFragment
(returned from getParentFragment()
) as a common for all fragments, but then, as in the case of Activity
, ViewModel.onCleared()
will not be called when the real parent fragment finishes.
There's really no way to do this.
Here is a code snippet from androidx.navigation.fragment.FragmentNavigator
:
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
// ...
final FragmentTransaction ft = mFragmentManager.beginTransaction();
// ...
ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
// ...
}
Under the hood, the FragmentManager
is used, which calls replace()
. Therefore, the child fragment is not added, but is replaced with a new one, so it will not be in getParentFramgent()
.
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