How do I disable back navigation and remove the back arrow on a Fragment, using Android Jetpack's Navigation component? - android

I am using Google's recommended single activity pattern with Android
Jetpack's Navigation component.
Fragment 1 is an authentication screen. After the user is authenticated and navigates to Fragment 2, I would like to make pressing of the Android back button close the app, and remove the back arrow in the app bar.
I have found methods, like onBackPressedDispatcher, to add / remove functionality from the back press, but nothing that also removes the back arrow.
I have also tried app:popUpTo="#+id/firstFragment" when navigating from Fragment 1 to Fragment 2, but that doesn't work either.
This should be possible to specify with a single line of code. Still trying to find. Any tips?

You need to remove fragment1 from back-stack when navigation to fragment2
fragment1
<fragment
android:id="#+id/fragment1"
android:name="packagenameforFragment1"
android:label="fragment1"
tools:layout="#layout/fragment_1" >
<action
android:id="#+id/action_Fragment1_to_Fragment2"
app:destination="#id/Fragment2_id"
app:launchSingleTop="true"
app:popUpTo="#+id/your_MainGraph_id"
app:popUpToInclusive="true" />
then when you navigate from fragment1 to fragment2 call this
findNavController(fragment).navigate(R.id.action_Fragment1_to_Fragment2)
to remove the back button from Fragment2 you can use this
in Activity onCreate()
val appBarConfiguration = AppBarConfiguration
.Builder(R.id.your_fragment2_id,R.id.any_other_ids_you_want)
.build()
then setup your toolbar like this
setupActionBarWithNavController(this, yourNavController, appBarConfiguration)

Related

How to achieve different animation while navigating to the same fragment with BottomNavigationView and navigation component?

I have the below screens:
Bottom Navigation stack: [A] [B]
Root fragments: [HomeFragment], [SettingsFragment]
Nested fragments: [ProfileFragment]
I want to achieve different animation while navigating to ProfileFragment:
From SettingsFragment
From BottomNavigationView going from ProfileFragment to [A] (HomeFragment) and then back to [B] (ProfileFragment)
I am using navigation component alongside bottomNavigationView which supports multiple backstack.
While navigating to ProfileFragment from SettingsFragment, I am using the below transition animations in the navigation graph:
app:enterAnim="#anim/slide_right"
app:exitAnim="#anim/slide_left"
Everything is working fine, but I don't want to use slide_right animation when the user goes to HomeFragment and then goes back to the ProfileFragment via bottomNavigationView.
I am setting up the navController like so:
bottomNavigationView.setupWithNavController(navController)
Is there any way to have a different animation (e.g fade in, fade out) while navigating to ProfileFragment via bottomNavigationView but use my custom slide_right animation while navigating from SettingsFragment?
After a lot of trial and error, downgrading navigation component version
from 2.5.3 to 2.5.0 did the trick for me.
Without any further change, it started to work as expected.

Android clear backstack when leaving item tab BottomNavigation

I am using the last navigation library (version 2.4.0) which include multiple backstacks with a bottom navigation view.
The default behavior is that each backstack is saved when user go back to a tab.
I want to clear a backstack when the user leave a tab.
Based on this bottom nav with 3 tabs :
Tab1
Tab2
Tab3
ScreenA1
ScreenB1
ScreenC1
ScreenA2
ScreenB2
ScreenC2
From Tab1, if user:
go to ScreenA2
clic on Tab2 (ScreenB1 displayed)
go back to Tab1
here I want ScreenA1 and not ScreenA2 (default behavior of navigation library)
Thanks
You can turn off multiple backstacks for tabs, as it was before lib version 2.4. For this you should change
navView.setupWithNavController(navController)
to
NavigationUI.setupWithNavController(navView, navController, false)
In your navigation file add these properties app:popUpTo and app:popUpToInclusive to the action which navigates to ScreenA2.
<action
android:id="#+id/action_A1_to_A2"
app:destination="#id/A2"
app:popUpTo="#+id/A1"
app:popUpToInclusive="true"/>
for more information look at this link:
popUpTo example: circular logic
Try invoking the below before you invoke the method to navigate to TAB1.
navController.popBackStack(R.id.ScreenA1, false);
This worked for me wherein I had a similar scenario to you.
I just done by onNavigationItemSelected Listener
The id in your navigation.xml and menu.xml should be different
When you handle the click action for particular item, try this
R.id.home_bottom_item -> {navController.popBackStack();
navController.navigate(R.id.home_bottom_item)
}

NavigationUI not working correctly with bottom navigation view implementation

So I have a scenario in which I have 5 fragments attached with bottom navigation.
1. Home 2. Inbox 3. Search 4. Notification 5. Profile
So I have another fragment called (BarcodeDetail) to which I navigate from Home Fragment.
(Home -> BarcodeDetail)
Now from BarcodeDetail I navigate to Search Fragment
(BarcodeDetail -> Search)
But now if I select Home Fragment from BottomNavigationView It did not go to Home Fragment.
Rather than it selects the same current fragment that is Search Fragment.
(The log print the name of Search Fragment)
navController.addOnDestinationChangedListener((controller, destination, bundle) -> {
Timber.d("Destination -> %s", destination.getDisplayName());
});
private void setupBottomNavigation() {
NavHostFragment navHostFragment = (NavHostFragment)
getSupportFragmentManager().findFragmentById
(R.id.fragment_container_view);
if (navHostFragment != null) {
navController = navHostFragment
.getNavController();
NavigationUI.setupWithNavController(
binding.bottomNavigation, navController);
initDestinationListener();
}
}
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/sellerHomeFragment"
android:icon="#drawable/ic_home"
android:title="#string/home_text" />
<item
android:id="#+id/inboxFragment"
android:icon="#drawable/ic_chat"
android:title="#string/chats_text" />
<item
android:id="#+id/searchFragment"
android:icon="#drawable/ic_search"
android:title="#string/search_text" />
<item
android:id="#+id/sellerAlertFragment"
android:icon="#drawable/ic_notification"
android:title="#string/notifications_text" />
<item
android:id="#+id/sellerProfileFragment"
android:icon="#drawable/ic_profile"
android:title="#string/profile_text" />
</menu>
Please let me know if you need more information. Thanks
The navigation component lib enables multiple backstack support by default since the version 2.4.0, which has been also thoroughly demonstrated in this blog.
According to your sample code, your five top level fragments in your bottom sheet nav would now have their own backstack, the problem arises if you want to navigate from one of the top level fragments to another, like in your case (Home -> ... -> Search).
The reason why you cannot go back to Home is because you have never left Home's backstack and now Search is on top of it, i.e. the navigation component thinks that you're already on the correct fragment.
I would suggest to go through the navigation lib's recent changes first, rather then trying to quick fix as this is quite a behavior change and it could also affect other parts of your app's navigation.
Technically speaking, the way how the multiple back stacks for the bottom nav works is that every time you select a different bottom nav option, the implementation of onNavDestinationSelected(item: MenuItem, navController: NavController) would pop & save the state of all the fragments from the current top level fragment and then navigate to the other top level fragment by restoring it's state.
So, in order to "switch from one backstack to another" like in your case when you navigate from BarcodeDetail to Search, you need to adapt your current actions by at least these two options:
app:popUpTo="#id/id_of_home"
app:popUpToSaveState="true"
(where id_of_home is should be the id of Home and where I assume that this is the action with destination to Search).
With these two lines, you would pop & save the state of Home -> BarcodeDetail and navigate to Search, if you now would navigate back to Home via the bottom nav option, then you would restore the state and your navigation should be basically possible.
However, please note that you might need another flags like app:restoreState="true" and/or app:popUpToInclusive="true" depending on what you want achieve.
First clear the backstack by using this line of code
navController.popBackStack();
After that do navigate to your fragment by using this line of code
navController.navigate(R.id.your_fragment_id);
From this you will be able to navigate in bottom navigation without any issue.
I got the same problem in Navigation component 2.4.0, switching back to 2.3.5 works.
But after some testing I got it to work with 2.4.0 also if I add a listerner to BottomNavigationView and handle the navigation my self in addition to NavigationUI.setupWithNavController(..).
bottomNavigationView.setOnItemSelectedListener(item -> {
//logic like navController.popBackStack(R.id.homeFragment, false) and navController.navigate(item.getItemId(), null, options)
return true;
});
To Disable BackStack use the below Hacky Solution,
binding.bottomNavigationView.setOnItemSelectedListener {
NavigationUI.onNavDestinationSelected(it,findNavController(R.id.mainNavHost))
findNavController(R.id.mainNavHost).popBackStack(it.itemId, inclusive = false)
true
}
Tested with
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.0'
i am also faced same problem
just add app:popUpTo="#id/navigationHome" in your action tag
<fragment
android:id="#+id/navigationHome"
android:name="com.mymedisage.medisageapp.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home_new" >
<action
android:id="#+id/action_homeFragment_to_communityFragment"
app:destination="#id/navigationCommunity"
app:popUpTo="#id/navigationHome"
app:popUpToSaveState="true"
/>

Navigation graph scoped ViewModel not get cleaned on menu navigation (Drawer) events

I'm using Navigation components and integrated DrawerLayout for side navigation menu.
I have the following structure of navigation xml config:
<navigation
android:id="#+id/mobile_navigation"
app:startDestination="#id/nav_apple">
<navigation android:id="#+id/nav_apple">
<fragment
android:id="#+id/nav_apple_fragment"
android:name="com.my.AppleFragment"
tools:layout="...">
... other fragments and actions which share ViewModel ...
</navigation>
<navigation android:id="#+id/nav_orange">
<fragment
android:id="#+id/nav_orange_fragment"
android:name="com.my.OrangeFragment"
tools:layout="...">
</navigation>
</navigation>
And menu xml like this:
<menu>
<group>
<item
android:id="#id/nav_apple"/>
<item
android:id="#id/nav_orange" />
</group>
</menu>
In activity I have code very similar to the one from android docs (https://developer.android.com/guide/navigation/navigation-ui):
val navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(
topLevelDestinationIds = setOf(
R.id.nav_apple,
R.id.nav_orange
),
drawerLayout = binding.drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
binding.navView.setupWithNavController(navController)
In AppleFragment and other fragments of 'nav_apple' graph I'm using the following code to use navigation graph-scoped ViewModel:
private val model: MyViewModel by navGraphViewModels(R.id.nav_apple)
It allows me sharing data and works perfectly, but when I use side menu (Drawer) and navigate to another destination and back - ViewModel data persists, which is undesirable.
According to the docs Any ViewModel objects created in this way live until the associated NavHost and its ViewModelStore are cleared or until the navigation graph is popped from the back stack. and I thought this navigation drawer events are the case, but looks like I'm missing something.
As per the Principles of Navigation, the start destination (and, if that startDestination points to a <navigation> graph, its startDestination) of your graph is always on the back stack.
As the navigation graph itself remains on the back stack, the state and ViewModels attached to that navigation graph also remains.
With the upcoming work on multiple back stacks, every bottom navigation item will have its own back stack and swapping between items will automatically save the current stack and restore it back when you reselect it. Therefore you should expect your bottom navigation items to automatically keep their ViewModels once that it is released.

How to implement navigation with BottomNavigationView?

My application has 4 screens: Login, Home, Profile, Settings.
Login is the start screen. Then there is the Main screen where the BottomNavigationView switches 3 other screens. I made 2 navigation graphs: the first for the Login-Main fragments, the second for the Home-Profile-Settings fragments. The second graph was attached to the BottomNavigationView. And it works good.
bottomNavigationView = findViewById(R.id.bttm_nav);
NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController());
But when I want to log out and navigate from Profile back to Login, I cannot do that because these are fragments of different graphs. How can I implement this logic?
I think I need to combine my navigation into one graph.
I will be very appreciated if you share some code with similar flow.
My suggestion in a logout scenario is to have
a main_graph: works as an orchestrator
a logged_out_graph: contains all the fragments accessible by unauthenticated users
a logged_in_graph: contains all the fragments accessible by authenticated users
In order to move from the logged_in_graph to the logged_out_graph you can use a global action defined in your main_graph, something like:
<action
android:id="#+id/action_global_logged_out_graph"
app:destination="#id/logged_out_graph"
app:launchSingleTop="true"
app:popUpTo="#+id/main_graph"
app:popUpToInclusive="true" />
Call the action programmatically in a fragment of the logged_in_graph by using the fragment NavController:
findNavController().navigate(R.id.action_global_logged_out_graph)

Categories

Resources