How to navigate to fragment with arguments without recreating it? - android

I have a question. I'm using nav component for navigation. For example i have fragment A, B and C and bottomNavigation. I'm using
binding.bottomNavigation.setupWithNavController(navController)
For multiple backstack. But here is situation: Main frag is A. I'm moving to fragment B or C. I have buttons on fragments B and C which should lead me to fragment A with putted arguments in it so i'm using just:
findNavController().navigate(fragmentBDirections.fromFragmentBToFragmentA(argument))
But here is a problem. I'm recreating fragment A after this but i'm already have this fragment in backstack. So is it possible to find A in backstack and navigate to it without recreating? Is it possible to save backstack after that?

Your problem seems like an ideal case to use a sharedViewModel
Your button in B or C should pop and fallback to A after updating a property in the viewModel. On leaving, the viewModel is not destroyed because it is bound to the activity and is available for Fragment A.
Bonus is using LiveData so that the change is observed and updated automatically

I think SavedStateHandle might be helpful.

<Navigation>
<fragment
android:id="#+id/BFragment"
android:name="com.packageName.app.BFragment"
android:label="fragment_b"
tools:layout="#layout/fragment_b" >
<action
android:id="#+id/action_BFragment_pop_including_AFragment"
app:popUpTo="#id/AFragment"
app:launchSingleTop="true" />
</Navigation>
<Navigation>
<fragment
android:id="#+id/CFragment"
android:name="com.packageName.app.CFragment"
android:label="fragment_c"
tools:layout="#layout/fragment_c" >
<action
android:id="#+id/action_CFragment_pop_including_AFragment"
app:popUpTo="#id/AFragment"
app:launchSingleTop="true" />
</Navigation>
try it . it's my solution

Related

open a fragment in a different level of hierarchy with backstack in Android Navigation component

I am using Navigation Component in my project and :
I need to open a fragment in a different level of hierarchy, so that the back stack is created properly too
in my nav_graph.xml there is a hierarchy like this:
HomeFragment -> CollocationFragment -> ChapterFragment --[selfNavigate]--> ChapterFragment -> PlayerFragment
as you see one of my fragments navigates to itself and send an arguments each time like this:
<fragment
android:id="#+id/ChapterFragment ">
<action
android:id="#+id/action_chapterFragment_self"
app:destination="#id/ChapterFragment" />
<argument
android:name="chapterID"
app:argType="Long" />
</fragment>
in a nutshell, I need to open PlayerFragment from HomeFragment with an appropriate backstack (the hierarchy mentioned above) .
HomeFragment ---[my back stack]---> PlayerFragment
I know that it seems NavDeepLinkBuilder creates the backstack itself but I have no idea how to create a custom backstack by using Navigation component for my fragments in this case.
eventually I find a way...it's kind of a workaround but it works like a charm:
so, just call navigate function in your desire order to make the hierarchy backstack:
fun openPlayerFromHome(){
findNavController.navigate(R.id.HomeFragment)
findNavController.navigate(R.id.CollocationFragment)
findNavController.navigate(R.id.ChapterFragment,bundleOf("chapterID",id))
findNavController.navigate(R.id.PlayerFragment)
}

backstack does not clear while navigating from fragment to activity

Here is my code
<fragment
android:id="#+id/fragment1"
android:name="com.example.app.Fragment1"
android:label="SignatureFragment"
tools:layout="#layout/layout_fragment1">
<action
android:id="#+id/action_fragment1_to_main_activity"
app:destination="#id/main_activity"
app:enterAnim="#anim/slide_in_from_right"
app:exitAnim="#anim/no_anim"
app:launchSingleTop="true"
app:popEnterAnim="#anim/no_anim"
app:popExitAnim="#anim/slide_out_to_right"
app:popUpTo="#id/navigation_graph_id"
app:popUpToInclusive="true" />
</fragment>
<activity
android:id="#+id/main_activity"
android:name="com.example.app.MainActivity"
android:label="MainActivity"
tools:layout="#layout/activity_main" />
Now the code for navigation
findNavController().navigate(R.id.action_fragment1_to_main_activity)
When I navigate to activity and press back, the fragment is still there. I want to clear the backstack after opening the activity.
I tried to remove the animation and also tried with removing app:launchSingleTop, but no success.
Edit
Jetpack Navigation is intended to work with single activity and does not fully support activity navigation with parameters passed to actions
Thus to clear stack when navigating from one activity to another you will still need to call activity.finish()
Edit end
The thing is findNavController().navigate(R.id.action_fragment1_to_main_activity) wont work.
Try to navigate via navigate(#NonNull NavDirections directions). In your case it will look something like this
findNavController().navigate(
Fragment1Directions.actionFragment1ToMainActivity())
Hope it helps.

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