I use the navigation component
to do various screen transitions.
Pass the title data
from A fragment
to B fragment
at the same time as the screen is switched. (using safe args
)
In fragment B
, set the data received from A
.
And to keep the title data
even when the screen is switched, I set it in LiveData
in the ViewModel
.
But if you go back from fragment B
to fragment C
,
B
's title is missing.
Some say that because this is a replace()
method, a new fragment is created every time the screen is switched.
How can I keep the data even when I switch screens in the Navigation Component?
Note: All screen transitions used findNavController.navigate()!
fragment A
startBtn?.setOnClickListener { v ->
title = BodyPartCustomView.getTitle()
action = BodyPartDialogFragmentDirections.actionBodyPartDialogToWrite(title)
findNavController()?.navigate(action)
}
fragment B
class WriteRoutineFragment : Fragment() {
private var _binding: FragmentWritingRoutineBinding? = null
private val binding get() = _binding!!
private val viewModel: WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() }
private val args : WriteRoutineFragmentArgs by navArgs() // When the screen changes, it is changed to the default value set in <argument> of nav_graph
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.setValue(args) // set Data to LiveData
viewModel.title.observe(viewLifecycleOwner) { titleData ->
// UI UPDATE
binding.title.text = titleData
}
}
UPDATED Navigation Graph.xml
<?xml version="1.0" encoding="utf-8"?>
<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_graph"
app:startDestination="@id/calendar">
<!-- fragment A -->
<dialog
android:id="@+id/bodyPartDialog"
android:name="com.example.writeweight.fragment.BodyPartDialogFragment"
android:label="BodyPartDialogFragment"
tools:layout="@layout/fragment_body_part_dialog">
<action
android:id="@+id/action_bodyPartDialog_to_write"
app:destination="@id/write"/>
</dialog>
<!-- fragment B -->
<fragment
android:id="@+id/write"
android:name="com.example.writeweight.fragment.WriteRoutineFragment"
android:label="WritingRoutineFragment"
tools:layout="@layout/fragment_writing_routine">
<action
android:id="@+id/action_write_to_workoutListTabFragment"
app:destination="@id/workoutListTabFragment" />
<argument
android:name="title"
app:argType="string"
android:defaultValue="Unknown Title" />
</fragment>
<!-- fragment C -->
<fragment
android:id="@+id/workoutListTabFragment"
android:name="com.example.writeweight.fragment.WorkoutListTabFragment"
android:label="fragment_workout_list_tab"
tools:layout="@layout/fragment_workout_list_tab" >
<action
android:id="@+id/action_workoutListTabFragment_to_write"
app:destination="@id/write"
app:popUpTo="@id/write"
app:popUpToInclusive="true"/>
</fragment>
</navigation>
UPDATED ViewModel( This is the view model for the B fragment.)
class WriteRoutineViewModel : ViewModel() {
private var _title: MutableLiveData<String> = MutableLiveData()
val title: LiveData<String> = _title
fun setValue(_data: WritingRoutineFragmentArgs) {
_title.value = _data.title
}
}
Error
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.writeweight, PID: 25505
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at androidx.navigation.NavArgsLazy.getValue(NavArgsLazy.kt:52)
at androidx.navigation.NavArgsLazy.getValue(NavArgsLazy.kt:34)
at com.example.writeweight.fragment.WriteRoutineFragment.getArgs(Unknown Source:4)
at com.example.writeweight.fragment.WriteRoutineFragment.onViewCreated(WriteRoutineFragment.kt:58)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2987)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:546)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2106)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8512)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: java.lang.IllegalArgumentException: Required argument "title" is missing and does not have an android:defaultValue
at com.example.writeweight.fragment.WriteRoutineFragmentArgs$Companion.fromBundle(WriteRoutineFragmentArgs.kt:26)
at com.example.writeweight.fragment.WriteRoutineFragmentArgs.fromBundle(Unknown Source:2)