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.
Related
I'm working with the fragments navigation and have a problem removing one from the back stack. Here're specifics:
I have 3 independent fragments (let's name them A1, A2, and A3) and each of them can navigate to fragment B using findNavController().navigate(), which after some actions navigates to fragment C using the same method.
What I need is when the back arrow is pressed I return to the A-fragment(which I started from) avoiding fragment B.
I've tried using app:popUpTo, and popBackStack but it hadn't worked. Also, I'm highly not recommended to use FragmentManager
You can use
findNavController().popUp(2)
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)
I am making A Trivia app following Udacity Android with Kotlin guide.
In Navigation Graph there are three destination screen; TitleFragment, GameFragment,GameOverFragment and GameWonFragment. (Images for code are included)
While constructing Navigation graph, we are setting up for system back key by setting popUpTo behavior attribute.
The guide says us to setup popUpTo (inclusive) attribute for action connecting GameFragment to GameOverFragment and GameWonFragment which will lead back to TitleFragment by popping up fragments including GameFragment on system back button hit. I understood till here.
But again the guide tells us to set action tag and popUpTo attribute from GameOverFragment and GameWonFragment to GameFragment. This time we wet popUpTo attribute(exclusive) for popping upto TitleFragment on system back key hit.
Both set up are doing same thing; taking us to TitleFragment on System back key hit. So, why setting up 2 times is necessary?
As the Documentation say :
When navigating using an action, you can optionally pop additional
destinations off of the back stack. For example, if your app has an
initial login flow, once a user has logged in, you should pop all of
the login-related destinations off of the back stack so that the Back
button doesn't take users back into the login flow.
To pop destinations when navigating from one destination to another,
add an app:popUpTo attribute to the associated element.
app:popUpTo tells the Navigation library to pop some destinations off
of the back stack as part of the call to navigate(). The attribute
value is the ID of the most recent destination that should remain on
the stack.
You can also include app:popUpToInclusive="true" to indicate that the
destination specified in app:popUpTo should also be removed from the
back stack.
When using popUpTo , all destinations from current destination to the destination with the id in popUpTo will be deleted , except that destination with id which will remain in the back stack .
And when using popUpToInclusive = true , then all destination and also the destination with the id in popUpTo will be deleted from the back stack !
This is more clear explain in detail about this , in the official documentation .
Enjoy !
I'm trying to implement circular logic with the Navigation component but I'm concerned that I'm not doing it right and lifecycle methods are unnecessarily being called.
I have 1 activity that has 3 fragments. Navigation between the fragments looks like this:
A -> B -> C -> back to A etc..
A and B are regular Fragments while C is a DialogFragment. C has two buttons - Cancel and Done. If Cancel is pressed, a navigation action is called (using findNavController().navigate(<action>)) and the app will show A. If Done is pressed, C is dismissed and the app will show B and the user can then return to A by pressing back. This all works as I expect, however...
My concern is that each route back to A results in different lifecycle methods being called in A. If navigation returns to A after the user accepts C and presses back on B, onCreateView(), onViewCreated(), and onResume() is called by A. BUT, if navigation returns to A after C is cancelled, A calls many more lifecycle methods (onAttach(). onCreate(), onCreateView(), onViewCreated, onResume(), onDestroy(), onDetach()). Why is there a difference? Why is onCreate() being called again in A? Shouldn't it just use the existing instance of A instead of creating a new one?
I can't figure out why it's doing this or if it's even something I should be concerned about. I'm confident that the stack is appropriately managed as the user navigates between fragments because the navigation action between C and A uses the popUpTo and popUpToInclusive attributes (as recommended here in the docs). I've also tried setting the launchSingleTop attribute in the action between C and A but I get the same behaviour (extra lifecycle methods being called in A).
Here is the xml for the C fragment:
<dialog
android:id="#+id/C"
android:name="C"
... >
<action
android:id="#+id/C_to_A"
app:destination="#id/A"
app:popUpTo="#id/A"
app:popUpToInclusive="true" />
</dialog>
I call action C_to_A from C when the user presses the cancel button.
Any help clearing up my confusion would be very welcome.
An action has two steps:
popping any destinations set via popUpTo / popUpToInclusive
Navigating (i.e., creating a new instance of) a destination set via app:destination
Actions can be any combination of just the pop, just the navigate, or both - the UI in the Navigation Editor when you right click a destination and select New Action will give you each of these options.
Therefore if you want an action that only pops back to a destination you know is on the back stack, then you can remove your app:destination attribute and only pop:
<action
android:id="#+id/C_to_A"
app:popUpTo="#id/A"
app:popUpToInclusive="false" />
Note that by using app:popUpToInclusive="false", you ensure that A will be on the top of the stack after the action is executed.
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