I'm trying to implement session expiry in my app. So when the user token has expired, I want to navigate to the Login Fragment and clear all existing destinations on the back stack. I know this is achievable with popUpTo and popUpToInclusive but a lot of conditional navigation is going on and the destination to popUpTo can't be determined at the point of navigating to the Login Fragment. Is there a way to simply navigate to a new fragment and clear every other destination on the back stack?
You can use app:launchSingleTop="true" in fragment action in navigation graph xml file.
Related
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 have a nav structure like so:
StaringFragment -> nestedNavGraph
|
HomeFragment -> SecondFragment
I need to make it so that after the user navigates away from StartingFragment to the nestedNavGraph the user can no longer hit the back button to return to the StartingFragment.
I am basically performing a permissions check, and if the check succeeds I navigate from the StartingFragment to the nestedNavGraph with this code:
findNavController(R.id.nav_host_frag).navigate(R.id.nestedNavGraph)
Then, on the action arrow between the StartingFrag -> nestedNavGraph in the nav graph editor, I set popUpTo to HomeFragment and inclusive to true. Did I misunderstand the documentation?
What's the proper way to remove the StartingFragment from the backstack as soon as I navigate away from it?
As per the documentation, you set app:popUpTo to the destination you want to pop - you don't want to pop the HomeFragment, you want to pop the StartingFragment, so you should set your app:popUpTo to the ID of the StartingFragment.
Note that as per the Principles of Navigation, you should always have a fixed starting destination - you shouldn't be using the start destination for conditional navigation - many of the APIs in Navigation, such as NavigationUI, assume you're correctly following the Principles of Navigation and that the start destination of your graph remains on your back stack.
Keep in mind that Android will restore your state (including what fragment you were at) when returning to your activity (say, after the user manually revoked the permissions you asked for) - your Fragment itself (or the activity if the permission really is globally required on every single screen in your whole app) should always be checking for permission - you can't rely on your StartingFragment always being called.
I haven't used the nav lib yet, but from reading this bit in the documentation maybe you just have a typo and need to replace popTo with popUpTo?
To pop destinations when navigating from one destination to another, add a 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.
I have an BottomNavigationView and I would like to use it to navigate between three or four different fragments. If I instantiated one of the fragment I don't want do it again for same fragment I just want to reuse it.
NavOptions class providing launchSingleTop property to avoid to create new instance if that kind of instance already on top of the back stack. But how about singleTask or singleInstance option for back stack of fragments in navigation controller? If I no need to create new instance of fragment if it's already in the back stack and I just want to bring it on top? How to achieve this with Navigation Architecture Component of Jetpack?
I just stumbled across this problem and I finally found a solution. You can use popUpTo to achieve it.
So for example, you have fragment Home, Login, and Register.
From fragment Home, you navigate to Login, and then to Register, and to Login again. To prevent duplicate Login fragment, you should set popUpTo="#id/login_fragment" to the action.
<action
android:id="#+id/action_navigation_register_to_navigation_login"
app:destination="#id/navigation_login"
app:popUpTo="#id/navigation_login" />
Stack visualization:
Register
____
Login ==> Login ==> Login
____ ____ ____
Home Home Home
When I open the app from a deeplink (user clicks on URL) and press back button I expect user to navigate to a previous fragment in my navigation graph but it just exits the app.
The documentation says that back navigation should work the same way as if it the user got to that screen naturally.
Can I somehow specify the desired backstack in my navigation graph? Or can be backstack formed automatically after a deeplink? For older version of the library I found out that after back press it should navigate to the root of my navigation graph but that does not happen.
I’m using the navigation library from Android architecture components (version 1.0.0-beta01).
I found out what's going on here, for explicit deep links its supposed to go to a new back stack which is what you app would have if a user had naturally navigated to the view not the existing back stack (existing stack is cleared.
When a user opens your app via an explicit deep link, the task back stack is cleared and replaced with the deep link destination. When nesting graphs, the start destination from each level of nesting—that is, the start destination from each element in the hierarchy—is also added to the stack. This means that when a user presses the Back button from a deep link destination, they navigate back up the navigation stack just as though they entered your app from its entry point.
For implicit its a bit strange. You can make it do what explicit does but setting Intent.FLAG_ACTIVITY_NEW_TASK otherwise the back button and the navigation up button do two separate things:
The back button will do as you might expect, it will go back in your apps existing back stack and load that fragment.
The up button however will clear the a back stack and make a new one as if it was an explicit link.
If the flag is not set, you remain on the task stack of the previous app where the implicit deep link was triggered. In this case, the Back button takes you back to the previous app, while the Up button starts your app's task on the hierarchical parent destination within your navigation graph.kquote
Source: Android Documentation
As describe here back button should return to the previous fragment, you can set it manually in Java like this: button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));
In Kotlin like that: button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null))
The Android system maintains a back stack containing the last visited destination. The first destination of your app is placed on the stack when the user opens the app. Each call to the navigate() method puts another destination on the top of the stack. Conversely, pressing the Up or Back button calls the NavController.navigateUp() and NavController.popBackStack() methods, respectively, to pop the top destination off of the stack.
Make sure that you are using NavHostFragment and not <fragment> in your hosting fragment activity.
I have 3 Activities: Main, Login, Wizard.
For each Activity I have separate Navigation Graph with fragment destinations.
Main Nav Graph has also Login Activity destination to launch Login screen on logout.
App seems to work correctly when launched from Action.MAIN launcher intent.
BUT I experience problem when using Deep Link. I would like to add URI to Login Nav Graph (Change Password Fragment). This Nav Graph uses separate NavHostFragment. I have used 2 approaches.
Directly add Deep Link to Change Password Fragment Destination in Login Nav Graph -> Deep Links navigate only to START DESTINATION (Login Fragment Destination). Here also Back button does NOT work correctly, i.e. finishing app instead returning to Main Nav Graph Main Destination.
Add Deep Link to Login Activity Destination in Main Nav Graph and again and here Back button does work correctly, I as expected landed on Login Fragment Destination. BUT here I want to manually navigate to Change Password Fragment Destination and I experience another problem, i.e. the Intent Action.View with Uri data is not delivered into this Login Activity destination. It is only delivered to Main Activity which host Main Nav Graph.
To sum up. I think this Deep link behaviour between multiple Nav Graphs connected by Activity Destinations does NOT work as it should. I do not know whether there are any solutions to this.
Can I retrieve Deep Link Arguments (Uri data) from NavController somehow? Or the only solution is to get it from getIntent().data? Here as I say this Intent is not forwarded to final destination Activity, but only to first Activity in the created stack of Activities.
Now I returned to manually handling Deep Links without Android Navigation Architecture as it seems useless if there is more complex navigation structure than single Nav Graph with single Activity and only inner Fragment navigation.