Is there some way I can prevent certain destinations in my navgraph from being added to the backstack?
For example, lets say I have 5 fragment destinations: A, B, C, D and E.
navgraph looks like this: A -> B -> C -> D -> E -> A (its a loop)
Now B, C, D and E are fragments that I dont want the user to be able to navigate back to. If the user navigates back at any point in the navgraph, I want them to go to the previous A fragment.
I'm currently doing this by overriding onBackPressed with complicated logic that keeps popping the back stack until a fragment A is reached.. It works but it feels far from optimal. If I could simply prevent B, C, D and E fragments from being added to the backstack it would be much simpler.
SOLUTION 1:
The app:popUpTo and app:popUpToInclusive are exactly what you may be looking for
Via the documentation,the xml attributes will
Pop up to a given destination before navigating. This pops all non-matching destinations from the back stack until this destination is found
Let me explain this to you with three fragments A , B, C
A (start) -> B -> C
if I want to navigate to C
and pop all including A(startscreen) then the code will be:
<action
android:id="#+id/B_to_C"
app:destination="#id/C"
app:popUpTo="#+id/A"
app:popUpToInclusive="true" />
if I want to navigate to C and pop all excluding A then the code will be:
<action
android:id="#+id/action_B_to_C"
app:destination="#id/C"
app:popUpTo="#+id/A"/>
SOLUTION 2:
Using the popBackStack method of the NavController class which does the same job as the onBackPressed but in an elegant way
If you have three fragments : A, B, C
And you are currently in fragment C and would like to go back to A before navigating anywhere(or not navigating anywhere if you please)
NavController controller = Navigation.findNavController(view);
controller.popBackStack(R.id.fragmentB, true);
OR
NavController controller = Navigation.findNavController(view);
controller.popBackStack(R.id.fragmentA, false);
where true or false will indicate if you actually want the destination to stay(false) or be popped off the stack too (true)
Related
I'm using Navigation Component to navigate in my app. I'm working with tree structure and I'm navigating users node by node -> Node == NodeFragment. But now I'm struggling with back navigation to particular node.
E.g.: Image navigation stack with look like this A -> B -> C -> D when user wants to navigate from node D directly to node B I want to popup fragment C, D and go directly to B?
E.g.: You can imagine something like navigating trough folder structure where you are navigation the same fragment again and again. And then you want to go back to particular folder which is not parent of the current one.
But I don't know ho to achieve it with Navigation Component. For navigation I'm using self action. Thanks in advance.
<fragment
android:id="#+id/nodeFragment"
android:name="com.example.NodeFragment">
<action
android:id="#+id/action_chatbotBuilderChildrenFragment_self2"
app:destination="#id/nodeFragment" />
</fragment>
So I found some kind of workaround solution which is pretty simple. It seems that calling NavController.navigateUp() multiple times in sort period of time works just fine. I'm not sure if this is the right solution but it solves my issues.
I was also supprise that even my fragment transition wasn't break after call NavController.navigateUp() multiple times.
Code example:
repeat(numberOfPopUps) {
findNavController().navigateUp()
}
Go into your navigation graph, click on the action (the arrow line) on C -> D, on the right panel (make sure in design mode instead of split or code mode) you will see pop up tp option, then choose Fragment B, that's it. So whenever you are in Fragment D and you click the Back key, the app will nagivate back to Fragment B.
In my app I have one Activity and the rest are fragments. Right now I am trying to get run as follows. Let's say, I have Fragments A (Home), B, and C. The scenario is this:
I navigate from A to B that shows me some data that I pass from A by Safe args. Then I navigate from B to C to edit data shown in B. Then I go back from C to B and B shows me the edited data in C. Lastly, I go back to home. That's all.
I pass data by Safe args, but the problem is, when I come back to B from C, I cannot navigate back to A because the navigateUp() brings me back to C instead of going to A.
Navigating methods:
From A to B: Navigation.findNavController(requireActivity(), R.id.nav_host_fragment).navController().navigate(HomeFragmentDirections.actionNavHomeToNavB(some_string_data))
From B to C: Navigation.findNavController(requireActivity(), R.id.nav_host_fragment).navController().navigate(BFragmentDirections.actionNavBToNavC(string_data_in_B))
From C to B: Navigation.findNavController(requireActivity(), R.id.nav_host_fragment).navController().navigate(CFragmentDirections.actionNavCToNavB(edited_string_data_from_B))
From B to A: Navigation.findNavController(requireActivity(), R.id.nav_host_fragment).navController().navigateUp()
I also tried to use navigateUp() before going back to B, which throws exception that the destination does not exist and also, I tried to use popBackStack() to take the C from stack out so that the navigateUp() in B can bring me back to Home. No success yet.
My app is working with the navigation component. In this case, I am have four fragments loading sequence is A->B->C->D. In fragment D, when I finish it all, I wanna go back directly in fragment A and remove fragments B,C,D at the same time.
Anyone have solution for this case? Thanks for your respone !
You can solve the destination situations in the documentation by examining them.
link 1
link 2
With each navigation action, a destination is added to the back stack. If you were to navigate repeatedly through this flow, your back stack would then contain multiple sets of each destination (A, B, C, A, B, C, A, and so on). To avoid this repetition, you can specify app:popUpTo and app:popUpToInclusive in the action that takes you from destination C to destination A, as shown in the following example:
<fragment
android:id="#+id/c"
android:name="com.example.myapplication.C"
android:label="fragment_c"
tools:layout="#layout/fragment_c">
<action
android:id="#+id/action_c_to_a"
app:destination="#id/a"
app:popUpTo="#+id/a"
app:popUpToInclusive="true"/>
After reaching destination C, the back stack contains one instance of each destination (A, B, C). When navigating back to destination A, we also popUpTo A, which means that we remove B and C from the stack while navigating. With app:popUpToInclusive="true", we also pop that first A off of the stack, effectively clearing it. Notice here that if you don't use app:popUpToInclusive, your back stack would contain two instances of destination A.
You can use Navigation.findNavController(requireView()).popBackStack(R.id.profileFragment, false)
profileFragment is the id of the destination you need to go to.
false when you don't need to pop the destination profileFragment from the stack. Otherwise, true.
I am using navigation component so I'm not using a FragmentManager. Let's say I have fragments A, B.
If user navigates A -> B -> A -> B and then presses the back button I want the backstack to just be A instead of A -> B -> A. In other words don't add a Fragment if it already exists in the backstack. Is this possible without using FragmentManager?
Yes you need to use setPopUpTo
Here is a good article to help you out
I have fragments: A, B, C, D. I navigate A -> B . Fragment B gets and saves state from arguments. Then I navigate B -> C. And then C -> D. When I call two times findNavController().popBackStack() I get correct behavior: D -> B and B still has correct state. It works because fragment B has never been destroyed, just its view. And then view is recreated when coming back. But calling two times popBackStack() isn't recommended action. We should instead use the action with app:popUpTo and app:popUpToInclusive="true":
<action
android:id="#+id/action_fragmentD_to_fragmentB"
app:destination="#id/fragmentB"
app:popUpTo="#+id/fragmentB"
app:popUpToInclusive="true" />
But it forces fragment B to be destroyed completely and then recreated. Bu with no previous state.
In other words I want to achieve the same behavior as with Activities when used FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_SINGLE_TOP: https://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP
There's no requirement to have an app:destination="#id/fragmentB" on an action if you don't want to navigate to a new instance of fragmentB (since that's what app:destination does). Therefore you can use:
<action
android:id="#+id/action_fragmentD_to_fragmentB"
app:popUpTo="#+id/fragmentB" />
This is identical to calling popBackStack(R.id.fragmentB, false) - i.e., pop back to fragmentB, but don't pop fragmentB itself.
You can use the class SingleLiveEvent to retain the previous state since it emits the data only once whenever required.
learn more about SingleLiveEvent:
https://blog.mindorks.com/observe-event-only-once-using-single-live-event
https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150