Conditional back navigation with Android Navigation Component - android

Im making navigation in my app using navigation component from jetpack.
I have 3 screens in my app, all of them implemented via Fragment:
- search screen
- list screen
- detail screen
When user presses search (on search screen), app navigates to list screen loads results and displays them. User selects one of the results and navigates to detail screen. If there is only one result, app navigates from list screen to detail screen automatically, effectively skipping list screen.
The problem is back navigation: when there was multiple results I need to navigate back to list screen, but if there was only one result I need to navigate back to search screen. I want just call navigateUp, but this will take me to list screen (in all cases) and then forward to detail screen if there is only one result.
When using FragmentTransaction directly, we can replace current fragment with and call addToBackStack only if we want to return to it later.
When using navigation component we can just navigate, and it behaves as replace + addToBackStack.
How can I achieve replace-without-adding-to-backstack behavior using architecture component?

Finally, I managed how to achieve it.
Now I have two actions with the same start and destination, their only difference is popUpTo attribute.
<action
android:id="#+id/action_listFragment_to_detailFragement"
app:destination="#id/detailFragment"/>
<action
android:id="#+id/action_listFragment_to_detailFragement_pop"
app:destination="#id/detailFragment"
app:popUpTo="#id/listFragment"
app:popUpToInclusive="true" />
action_listFragment_to_detailFragement is used when user manually performes transition and it should return to list screen later
action_listFragment_to_detailFragement_pop is used when transition performed automatically and list screen should not be shown after return.

In Navigation Component, you can use the method NavController.popBackStack(int action) which works similar to addToBackStack(null).

Just use { Safe-Args Directions } instead of ID:
override fun navigateTo(navController: NavController) {
val action =
FirstFragmentDirections.actionFirstFragmentToSecondFragment()
navController.navigate(action)
}

Related

Android Navigation: removing an activity from the backStack

This question is related specifically to the androidx.navigation library.
I split up my primary graph into 2 graphs because I wanted to have one with a bottom nav with the fragments above it and one without. Instead of using <include... I added the activity to the first graph
<activity
android:id="#+id/Activity2"
android:name="com...Activity2"
android:label="Activity2" />
This all works nicely, but I also have a splash screen in the first graph that checks if the user is authenticated and navigates them directly into the second graph. With a fragment I can just use the standard popTo and popToInclusive to manage the fragment backstack but I have not been able to figure out how to do this with two activities so that when the second activity is launched the first is killed and removed from the backstack so the user cannot navigate backwards.
Currently I am just handling it in the fragment where the navigation occurs
navController
.navigate(R.id.action_someFragment_to_anotherFragment)
requireActivity().finish()
and this works but it leaves room for error and I'd like to deal with it with the navigation library if possible.
Each individual NavController is totally independent from one another. While an <activity> destination allows you to use navigate() to go to an entirely separate activity (which may or may not use Navigation itself), Navigation itself will never finish() an activity as part of a navigate() call so you'd need to do that yourself.
Using multiple activities with different navigation graphs is not the recommended way to handle authentication in Navigation as per the the Navigating Navigation talk and this approach fails in many ways (such as deep linking and invalidation after process death/recreation) that are correctly handled by the guide for handling login. When using one NavController and the ability to listen for navigation events, you do not run into these issues.

Android Navigate Component navigate to sub graph

I hava two Fragment(OverviewFragment、PersonalFragment), and one sub graph(contains two Fragment :BucketsFragment and ObjectsFragment, BucketsFragment is start destination). They are bind to one BottomNavigateView.
Now, After navigate to the sub graph start destination(BucketsFragment), and continue to navigate to ObjectsFragment. Then navigate to OverviewFragment and finally return to the sub graph.
Now, I came to start destination(BucketsFragment), but actually what I want is ObjectsFragment. what should I do?
The whole process is shown here
I want to navigate to the sub graph that retains the previous state, instead of a new sub graph.
As per the material design guidelines for bottom navigation:
On Android: the app navigates to a destination’s top-level screen. Any prior user interactions and temporary screen states are reset, such as scroll position, tab selection, and in-line search.
So technically, this behavior is expected.
When you're on the Resources tab and go to the ObjectsFragment, the back stack is
DashBoardFragment -> BucketsFragment -> ObjectsFragment
When you go back to the Overview tab, the back stack becomes
DashBoardFragment
And no state is saved for Fragments that aren't on the back stack. That's why when you reselect the Resources tab, it is recrated from scratch and you get back to
DashBoardFragment -> BucketsFragment
There's an existing issue for supporting multiple back stacks which aims to bring support for saving the state of each tab separately, allowing the behavior you wish. As per that issue, this requires significant changes to how Fragments work (since they are the one saving the state of Fragments) as well as integration into Navigation itself.
That issue points out a temporary workaround, demonstrated in the NavigationAdvancedSample where each tab uses its own separate navigation graph and separate NavHostFragment, thus allowing each one to keep its own state independently from one another. This requires a bit different of a setup in the MainActivity and the help of a set of NavigationExtensions to get that working.
It is expected that all of that functionality, once the multiple back stacks work is done, to be folded into the Navigation library itself.

Navigation Component with Login / Register / Home flow

I would like to use the new Navigation Component for my next application but I can't quite wrap my head around the overall flow of navigation.
The Android team recommends a single activity as an entry point. They also suggest that conditional elements like a login / register should not be the entry point.
But then how do you display the login and register buttons if the entry screen is supposed to be the home screen?
Another idea is to use a Splash screen, have the logic there to determine if the user is already logged in, if so go to the home screen, if not show the login / register screen(s).
My other issue is with the single activity. My home screen would need to be a screen with a Bottom Navigation.
How do you tie all of this the "right way"? Do I need to have a separate navigation graph for the Home view, with the Bottom Navigation and the many screens that will flow from there?
All the examples I have found have been very straightforward, and the few I have seen with a Splash screen splitting into Home and Login have a very simple Home fragment, which in my case would be . more complex with the Bottom Nav.
Thanks.
One way to do this, you hide the bottom navigation in the login and popup the homepage when you navigate to the login page so the user would not be able to go back to the splash screen
1-you can hide the bottom naviagtion bar in the login fragment like this
val toolbar = activity!!.findViewById<Toolbar>(R.id.toolbar)
val bottombar = activity!!.findViewById<BottomNavigationView>(R.id.bottomNavigationView)
toolbar.visibility = View.GONE
bottombar.visibility = View.GONE
2- popup the splash fragment when you navigate to the login page
<action
android:id="#+id/action_splashFragment_to_loginFragment"
app:destination="#id/loginFragment"
app:popUpTo="#+id/splashFragment"
app:popUpToInclusive="true"/>
Hopefully it will work for you
Simple example =
Single activity, Multi fragment with bottom navigation bar and with Android Navigation comp.
Plan:
Show a splash screen with a timer like 3000 milis.
End of the milis navigate the user to login screen if already logged (check it on splash) navigate user to home screen
Now you can show a bottom nav. bar. For example "Home" "News"
You can handle backPresses in navigation. Handle for home (Disable to returning splash)
That's all folks!

Back vs. Up - Intended behavior

The navigation design guide explains:
When the previously viewed screen is also the hierarchical parent of the current screen, pressing the Back button has the same result as pressing an Up button—this is a common occurrence.
up vs back - navigation guide
I have a MainActivity A which opens another activity B when touching a navigation entry in the NavigationDrawer. Activity A is set to be the parent of activity B in the AndroidManifest: android:parentActivityName=".MainActivity"
I followed this android documentaion to add up navigation to activity B. It shows how to implement onOptionsItemSelected in activity B:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
// Respond to the action bar's Up/Home button
NavUtils.navigateUpFromSameTask(this)
return true
}
}
return super.onOptionsItemSelected(item)
}
When I press back from Activity B the state of Activity A was saved and the NavigationDrawer is opened. If I use the up navigation though, onCreate()of activity A is called and it lost its state (the drawer is closed etc.).
This is not the quoted "same result".
When I replace the NavUtils.navigateUpFromSameTask(this) with a simple finish() it has the same behavior as pressing back - the state of activity A is kept.
Naturally I would prefer the way using finish. So what is the intended behavior? Do the guides contradict each other or am missing something?
It is an unfortunate reality that Google leaves documentation up for longer than it is relevant, and will even post two different pieces of documentation that directly contradict each other.
In the case of the Up button, your link says
The Up button appears in the app bar and is used to navigate within an app based on the hierarchical relationships between screens. [...]
The Back button appears in the system navigation bar and is used to navigate, in reverse chronological order, through the history of screens the user has recently worked with. It is generally based on the temporal relationships between screens, rather than the app's hierarchy.
However, there is also this article, which says
When the system Back button would not exit your app, such as when you are on your own task and not on the start destination, the Up button should function identically to the system Back button.
So... which one should you trust?
I assert that you should trust the second one. The first one was posted years ago; I don't know its exact age, but you can tell that it's old because the screenshots all use the Holo theme. The second one, on the other hand, is part of Android's Architecture Components, so is significantly newer. In general, I'd go with the newest piece of documentation.
Additionally, I think that Google was wrong to say for all these years that the Up button should work differently from the Back button. As someone who spent a lot of time thinking about navigation in my app, I see where they were coming from, but real-world users always get confused when Up did something "different".
So I'd go ahead and just finish() your activity when the user presses the Up button, and not worry about those two articles you found.
I think the change of policy to suggest consistency between the up icon and the system back button is a good idea. However the advice that you should:
"navigate in reverse-chronological order through the history of screens"
Is too crude, or at least should be clarified what they mean by "screen".
eg. When you have a bottom nav bar, the back button / up icon should take you back up the hierarchy within your tab section before jumping to the section previously visited. And you should preserve the previous state when revisiting a tab section (which may be drilled down into a lower level screen).

Android Navigation library deep linking: How to synthesise backstack

Using Android Architecture's Navigation component, I have the following navigation graph
-> [Home] -> [Articles List] -> [Specific Article]
I also have a deeplink to [Specific Article]. When it is opened, navigating up currently goes to [Home].
I'd like to synthesise a backstack such that navigating up instead goes back to [Articles List] (and then on to [Home] if navigating again).
What is the Navigation way of doing this?
Per the NavDeepLinkBuilder documentation, Navigation uses the startDestination of the destination for the synthetic back stack. If you Group destinations into a nested navigation graph, both the startDestination of the nested graph and the startDestination of the root graph are added to the back stack. This gives you the ability to have [Articles List] as the startDestination of the nested graph to add it to your back stack.
However, it is strongly recommended to keep your synthetic back stack as small as possible - while a depth of 2 or 3 (as here) is fine, it is not recommended to go much beyond that level to avoid cases where users have to repeatedly tap and tap the back button to get back to the launcher.
The documentation implies that my original solution should work.
When a user uses the Back button from a deep link destination, they navigate back up the navigation stack just as though they entered your app from the app’s entry point.
In addition, ianhanniballake's answer doesn't produce expected results (the deeplinked fragment is not opened).
I have created an issue on google's tracker for both these problems: https://issuetracker.google.com/issues/79734195
I came across this thread while using navigation compose. The issue for me was that I called navController.popBackStack() instead of the correct navController.navigateUp(). After changing my calls to navigateUp(), everything works as expected. Maybe this helps someone with the same problem.

Categories

Resources