Passing data back to previous Fragment with Android Navigation Components - android

Imagine that FragmentA starts FragmentB on a button click. Passing some arguments to FragmenB is pretty straightforward:
val action = FragmentADirections.actionToFragmentB("some text")
findNavController().navigate(action)
But is there a way to pass data from FragmentB to FragmentA when the user navigates away from FragmentB? With other words, when FragmentB is destroyed as the result of back press.

In your Navigation UI xml:
<fragment
android:id="#+id/FragmentB"
android:name="com.example.FragmentB"
android:label="FragmentB"
tools:layout="#layout/FragmentA">
<action
android:id="#+id/action_FragmentB_to_FragmentA"
app:destination="#id/FragmentA" />
</fragment>
Like you want to navigate with some arguments back to the FragmentA from FragmentB, use this:
findNavController().navigate(
R.id.action_FragmentB_to_FragmentA,
bundleof("key" to <your_value>) // example: 100 (Int value)
)
Then you can just receive the argument in your FragmentA using arguments?.getInt("key") or if you pass String can get it with arguments?.getString("str_key") etc.

Related

how to navigate back to previous fragment using navigation component while sending data?

I have 4 Fragments , A, B,C.Fragment A will be the main Fragment, I will be navigating from fragment A to Fragment B then Fragment B return with a result to fragment A. Then I will navigate To fragment C from fragment A, and fragment c will do some operations and return a result to fragment A. Each time the fragment A will show and keep the result returned from each fragment
The navigation between fragment A to the other fragment is implemented using this code
val bundle = Bundle()
bundle.putBoolean("data", true)
findNavController().navigate(R.id.myAction, toVoicePassphraseRecognitionbundle)
My problem her is that to return to fragment A, i also use findNavController().navigate()but doing so will create a new fragment that will be added to the stack, so my question is how can i navigate back from fragment B to A, or C to A while keeping the view state as it is without creating a new Fragment A.
The problem is, you are setting Fragment A as destination in your action,
what it does is, it creates a new instance of the Fragment.
What you really want is to go back to, not go to.
So what we we do to go back, we pop our back stack.
<fragment
android:id="#+id/fragmentC"
android:name="com.sample.project.FragmentC"
android:label="FragmentC">
<action
android:id="#+id/navigate_C_to_A"
app:popUpTo="#+id/fragmentA"
app:popUpToInclusive="false" />
</fragment>
popUpTo is where you want to land
popUpToInclusive means do you want this fragment to pop from stack too?
And let say your flow is like A->B->C->A
But Fragment A is unknown, or B and C are the part of flow that can be used at many places, so the A will not be A every time.
In that case :
<fragment
android:id="#+id/fragmentC"
android:name="com.sample.project.FragmentC"
android:label="FragmentC">
<action
android:id="#+id/navigate_C_to_before_B"
app:popUpTo="#+id/fragmentB"
app:popUpToInclusive="true" />
</fragment>
popUpToInclusive is the key here

Android Jetpack navigation A ->B->C->A

I couldn't find anything online so here I am. I'm using the jetpack navigation component and I want to navigate from fragmentA to fragmentB, and then fragmentB will navigate to fragmentC, but when pressing hw back button I want to go back straight to fragmentA. is this possible with the current release?
This can be done with adding popUpTo to your action where you move from B -> C.
<fragment
android:id="#+id/fragmentB"
android:name="com.ballboycorp.anappaday.navigationtest.FragmentB"
android:label="fragment_b"
tools:layout="#layout/fragment_b">
<action
android:id="#+id/action_fragmentB_to_fragmentC"
app:destination="#+id/fragmentC"
app:popUpTo="#+id/fragmentA" />
</fragment>
What that means is
From B move to C and when user clicks back button, move back to A.
You should navigation to C using that action instead of giving destination id.
button.setOnClickListener {
findNavController().navigate(R.id.action_fragmentB_to_fragmentC)
}
Visually this is how it looks
With the Navigation Component you can handle onBackPressed on fragments. In your onViewCreated of fragment C just add this line of code:
requireActivity().onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
view.findNavController().popBackStack(R.id.fragmentA, false)
}
})

Kill fragment in navigation controller

My flow of fragment is like this
Main -> A -> B -> C ->A
In fragment c, it has a submit button which will return to A.
When I press back button in A, I want it back to Main. But it return to fragment c instead.
In fragment C, I use this
findNavController().navigate(R.id.action_c_to_a)
nav_graph.xml
<fragment
android:id="#+id/fragmentC"
android:name="xxx"
android:label="xxx">
<action
app:launchSingleTop="true"
app:popUpTo="#+id/fragmentA"
app:popUpToInclusive="true"
android:id="#+id/action_c_to_a"
app:destination="#id/fragmentA" />
</fragment>
Why not pop up to fragment A? You could just call findNavController().popBackStack(R.id.fragmentA, false) instead of navigating with an action.
try to look at my complete solution with removing/killing fragment from backstack.
Navigation Component set transition animation programmatically
You can override the back button behavior to do this.
Provide custom back navigation
The idea is to set a app:popUpTo without setting app:destination. Indeed, setting a app:destination will create a fragment and add it to the backstack which is not what you want.
To you can just remove :
app:destination="#id/fragmentA"
If you have this :
app:popUpTo="#+id/fragmentA"
And it will really pop the backstack until fragmentA

Pass data back to previous fragment using Android Navigation

I've started using Android Architecture Components (Navigation and Safe Args, View Models) along with Koin library.
Currently, I've got a problem with passing arguments between two fragments - I need to pass a string value from fragment A to fragment B, modify this value in fragment B and pass it back to fragment A.
I've found one possible solution to my problem - shared view models. Unfortunately, this approach has one problem because I can pass and modify values between screens, but when the fragment A navigate to another destination the value in the shared view model is still stored and not cleared.
Is there any different solution of passing and modifying data between fragments in Android Navigation? I want to avoid clearing this one value by hand (when the fragment A is destroyed).
Android just released a solution for this; Passing data between Destinations (Navigation 2.3.0-alpha02), basically, in fragment A you observe changes in a variable and in fragment B you change that value before executing popBackStack().
Fragment A:
findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>("key")?.observe(viewLifecycleOwner) { result ->
// Do something with the result.
}
Fragment B:
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
navController.popBackStack()
You can use Fragment Result API.
Fragment A -> Fragment B
In Fragment A :
binding.buttonGo.setOnClickListener {
setFragmentResultListener(ADD_LOCATION) { key, bundle ->
clearFragmentResultListener(requestKey = ADD_LOCATION)
val selectedLocationModel =
bundle.getParcelable<LocationModel>(SELECTED_LOCATION_MODEL)
this.selectedLocationModel = selectedLocationModel
}
navToFragmentB()
}
In Fragment B:
setFragmentResult(
ADD_LOCATION,
bundleOf(SELECTED_LOCATION_MODEL to selectedLocationModel)
)
goBack()
Do not forget to call clearFragmentResultListener() before create new one.
Currently, I've got a problem with passing arguments between two fragments - I need to pass a string value from fragment A to fragment B, modify this value in fragment B and pass it back to fragment A.
The theoretical solution really is to have the two fragments in a shared <navigation tag, then scope the ViewModel to the ID of the navigation tag, this way you now share the ViewModel between the two screens.
To make this reliable, it's best to use the NavBackStackEntry of the Navigation tag as both a ViewModelStoreOwner and SavedStateRegistryOwner, and create an AbstractSavedStateViewModelFactory that will create the ViewModel using the ViewModelProvider, while also giving you a SavedStateHandle.
You can communicate the results from FragmentB to FragmentA using this SavedStateHandle, associated with the shared ViewModel (scoped to the shared NavGraph).
You can try this solution
<fragment
android:id="#+id/a"
android:name="...">
<argument
android:name="text"
app:argType="string" />
<action
android:id="#+id/navigate_to_b"
app:destination="#id/b" />
</fragment>
<fragment
android:id="#+id/b"
android:name="...">
<argument
android:name="text"
app:argType="string" />
<action
android:id="#+id/return_to_a_with_arguments"
app:destination="#id/a"
app:launchSingleTop="true"
app:popUpTo="#id/b"
app:popUpToInclusive="true" />
</fragment>
and navigation fragment
NavHostFragment.findNavController(this).navigate(BFragmentDirections.returnToAWithArguments(text))
ianhanniballake`s comment has helped me solve a similar problem
1) Pass string from Fragment A to Fragment B with action_A_to_B and SafeArgs.
2) popBackStack to remove Fragment B.
navController.popBackStack(R.id.AFragment, false);
or
navController.popBackStack();
3) Then pass modified data from B to A with action_B_to_A.
EDIT.
Here you have some another solution

AndroidX navigation navigateUp twice

I am using the new AndroidX navigation framework.
I have a few fragments all linked in a navigation chain.
FragmentA --> FragmentB --> FragmentC
FragmentC has a Cancel button that should send me up all the way back to FragmentA.
Should I do the following:
on FragmentC call the method:
Navigation.findNavController(view).navigateUp();
then on FragmentB listen to some callback and using some passed parameter or argument trigger another navigateUp() function from FragmentB
or is there some method that will do the equivalent of navigateUpTwice()
What I ended up doing was
Navigation.findNavController(view).popBackStack(R.id.fragmant_a,false)
In your navigation.xml file under the action that you have created to navigate to starting fragment (FragmentA in your case) add the following
<action
....
app:popUpTo="#id/fragmentA"
app:popUpToInclusive="false"/>
This will pop up all the fragments until FragmentA and will exclude FragmentA since popUpToInclusive is set to false.
Edit:
The full form of your action under FragmentC tag of your navigation.xml file will be something similar to this:
<fragment
android:id="#+id/FragmentC"
android:name="com.yourdomain.FragmentC"
android:label="FragmentC">
<action
android:id="#+id/action_fragmentC_to_fragmentA"
app:destination="#id/fragmentA"
app:popUpTo="#id/fragmentA"
app:popUpToInclusive="false"/>
</fragment>
Try with this, it works for me.
findNavController().navigateUp()
findNavController().navigateUp()
You can set pop To FragmentA in your action from FragmentB --> FragmentC and when then you press back it goes to Fragment A instead of Fragment B
So I found myself in a situation where I had to navigateUp() twice, but with a twist: I could reach the view through several paths. Think of it like this:
Fragment A --> Fragment C --> Fragment D
Fragment B --> Fragment C --> Fragment D
In a normal situation, using the popTo xml attribute or the popBackStack() method would work. However it is unusable here. Fortunately, what you can do is:
val navController = NavHostFragment.findNavController(this)
navController.navigateUp()
navController.navigateUp
As other people have pointed out, this is absolutely not optimised performance-wise. In any situation where you can reach Fragment D through a single path, use popTo instead.

Categories

Resources