BottomNavigationView with NavController reusable fragments with back stack - android

I'm trying to implement this solution using Google Jetpack Navigation component and still struggle with back stack issue.
The problem I'm facing is a custom implementation of navigating method in FragmentNavigator class breaks existing logic of back stack.
if I try to add simple behavior like:
transaction.addToBackStack(tag)
then BottomNavigationView is not highlighting/selecting buttons properly for a current active fragment when the back button is pressed.
My question how to implement properly custom back stack using such approach that works correctly with BottomNavigationView and properly using the back pressed listener to override more advanced custom back stack that put right navigation in the same BottomNavigationView?
The naive approach would be override onBackPressed in activity and then just create own logic separately and manually use navController.navigate to simulate back stack functionality, but I would like to hear how this problem can be solved with BottomNavigationView, NavController, NavHostFragment, FragmentNavigator scope.

Related

Navigation resets when switching fragments

I have the following navigation xml on a BottomNavigationView, however when the user navigates inside the 1st fragment and then navigates to another fragment using the BottomNavigationView and then comes back to the initial fragment, it resets all the way to the default 1st fragment the navigation to a deeper fragment does not persist, i assume this is because the fragment gets recreated and from what i've looked at on other questions is that there is a way to save the data to re-set any text values on text fields or similar stuff, however i haven't seen if theres a way to avoid the fragment from being destroyed and re-created in order to make the navigation and whatever changes the user had previously made to the view persist.
Is there a way to do that, or should i use a savedInstance in such a way that it re-navigates to the previously navigated fragment?
Part of Navigation 2.4.0 is support for multiple back stacks - i.e., the ability for each tab of a bottom navigation bar to save its state, restoring that state when you reselect that tab.
As per that blog post:
If you’re using NavigationUI, our set of opinionated helpers for connecting your NavController to Material view components, you’ll find that multiple back stacks is enabled by default for menu items, BottomNavigationView (and now NavigationRailView!), and NavigationView. This means that the common combination of using navigation-fragment and navigation-ui will just work.
This means that if you are using the setupWithNavController methods, then upgrading to Navigation 2.4 will give you support for multiple back stacks immediately. You can verify that by going to your order fragment (thus building a back stack on that first tab), going to a different tab, then tap on the first tab again to reselect it.
Of course, it is your fragments state, not the instances themselves that are saved and restored. This means that each individual fragment must still save its state properly.

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.

Handling back button with Android Navigation with two navigation graphs

I have created a small sample app to test out the android navigation library. The general idea is that I want two tabs with their own navigation graph.
My main activity layout contains a BottomNavigationView with two NavHosts.
The ButtonNavigationView click listener takes care of showing one NavHost or the other and also calling NavigationUI.setupActionBarWithNavController with the selected controller to update the toolbar accordingly. Up navigation is working fine.
The problem I am facing right now is the back button.
There is a property app:defaultNavHost="true" which ensures that your NavHostFragment intercepts the system back button, but I want that to be on/off depending on the active graph. I couldn't find a way to change it :(
I know I can override onBackPressed() but I am trying to find a way for the library to do that work for me letting it know which graph is active.
As per the NavHostFragment source, all that app:defaultNavHost="true" does is call through to setPrimaryNavigationFragment(), which is how FragmentManager knows which child Fragment to send back button events to.
Therefore, when switching to a new Fragment, you can add setPrimaryNavigationFragment() to your FragmentTransaction to get the same behavior.

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.

Fragment navigation in menu

I have a common layout where I have a menu in a DrawerLayout and these menu items replace the visible main fragment in my Activity. The top menu item represents my Home screen(fragment A). When I press something else(fragment B) in the menu I replace A with B and add the transaction to the back stack (A -> B). If I select fragment C from the menu I would like the back stack now to be A -> C. In order to do that I call popBackStack() and then replace A with C.
My issue here is that I would like the transition between B and C to be smooth but when I call popBackStack(), fragment A is visible for a split second before it's replaced with C. So what I want is basically creating a FragmentTransaction where I can pop the back stack without committing it before I replace A with C.
To clarify: I should note that when you are in fragment B you could select something there that would take you deeper so the stack would then be A -> B -> B1. If I press C from the menu at this point I need to clear the backstack and show C WITHOUT flashing A in the process.
Does anyone know if that is possible or does anybody have any other solution to my issue ?
Don't add any of fragment in back stack. Just replace one with another. In this way we will skip to call popBackStack().
Not sure if you've solved this issue, but I have been working on a library that should help you solve your navigation issues!
Since the implementation of the ChildFragmentManager it has given us flexibility in creating back stacks within individual fragments. I am leveraging this in the library. It's accessible in jcenter repository, please see the ReadMe for implementation details!
https://github.com/DMCApps/NavigationFragment
Basically you will need to use a SingleStackNavigationManagerFragment as your base fragment. When the menu item from A->B is clicked, call present(B) on the navigation manager (or the within FragmentA). When you want to add C to the stack but remove B, you can call clearNavigationStackToRoot. This will clear the current navigation stack to the first fragment that you added to the manager (aka FragmentA) without any navigation. Then present(C). This will cause no animation to occur for the clearNavigationStackToRoot, but will show an animation for the present(C).
If you need more clarification I can add a sample to my repository for you. The more samples the more resources people will have to utilize my library!
I have added an example of what you need under the DrawerClearStackExample package in the sample application for the library.
Thanks,
DMCApps

Categories

Resources