I am trying to clear all fragments from the back stack when the home fragment is displayed.
In the navigation graph I added this action
<fragment
android:id="#+id/loginFragment"
android:name="com.test.navTest.ui.fragment.LoginFragment"
android:label="fragment_login"
tools:layout="#layout/login_fragment"
>
<action
android:id="#+id/action_loginFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#+id/homeFragment"
/>
</fragment>
and In the login fragment. I tried
findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToHomeFragment())
and
findNavController().navigate(R.id.action_loginFragment_to_homeFragment)
As I understand from the documentation here https://developer.android.com/guide/navigation/navigation-navigate
that popUpTo clears all the stack until the destination. But nothing is cleared when I press back.
UPDATE:
I changed this action to popUpTo=Login. It removed only the login fragment and not the 2 fragments before it. Should I add any parameter to the action of the fragments before the login
<action
android:id="#+id/action_loginFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#+id/loginFragment"
app:popUpToInclusive="true" />```
What worked for me on clearing all fragments in the backstack is to add the graph id in the popUpTo value
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/loginFragment"
android:name="com.test.navTest.ui.fragment.LoginFragment"
android:label="fragment_login"
tools:layout="#layout/login_fragment">
<action
android:id="#+id/action_loginFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#+id/nav_graph"
app:popUpToInclusive="true" />
</fragment>
When I added popUpTo = #+id/login_fragment. It removed the login fragment only.
Related
I am using navigation architecture component in my application.
Suppose I have fragment A as start destination.
I have push fragment B and Fragment C via navigation controller.
My navigation back stack is A-> B-> C.
Then I navigate to Fragment A with below action
<action
android:id="#+id/fragment_A_type"
app:destination="#id/fragment_a"
app:popUpTo="#id/fragment_a"
app:popUpToInclusive="true" />
Now My navigation back stack should be A.
But if check
parentFragmentManager.fragments back stack is
A->A
Also the swipeBackDismiss of activity is not working.
my navigation.xml file
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph">
<fragment
android:id="#+id/FirstFragment"
android:name="com.example.wearexample1.FirstFragment"
android:label="#string/first_fragment_label"
tools:layout="#layout/fragment_first">
<action
android:id="#+id/action_FirstFragment_to_SecondFragment"
app:destination="#id/SecondFragment" />
</fragment>
<fragment
android:id="#+id/SecondFragment"
android:name="com.example.wearexample1.SecondFragment"
android:label="#string/second_fragment_label"
tools:layout="#layout/fragment_second">
</fragment>
<action
android:id="#+id/action_global_activity_type"
app:destination="#id/FirstFragment"
app:popUpTo="#id/FirstFragment"
app:popUpToInclusive="true" />
</navigation>
I'm using Android Jetpack Navigation Component for my app. It has Two screen (Fragment) which are homeFragment and loginFragment. The homeFragment is the start destination. After logout from homeFragment to loginFragment, the navigation back button shows up, it will back to homeFragment again when click, that's not correct. I expect the back button not shown in loginFragment, and if user press system back app exit. The nav_graph.xml is setting properly as this post was told:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_graph"
app:startDestination="#id/homeFragment">
<fragment
android:id="#+id/loginFragment"
android:name="cn.helptool.qxscales.LoginFragment"
android:label="Login" >
<action
android:id="#+id/action_loginFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#id/nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/homeFragment"
android:name="cn.helptool.qxscales.HomeFragment"
android:label="Home">
<action
android:id="#+id/action_homeFragment_to_loginFragment"
app:destination="#id/loginFragment"
app:popUpTo="#id/nav_graph"
app:popUpToInclusive="true" />
</fragment>
</navigation>
After read android docs
Add both of loginFragment and homeFragment as top level destinations, the problem fixed.
val appBarConfiguration = AppBarConfiguration.Builder(R.id.loginFragment, R.id.homeFragment).build()
setupActionBarWithNavController(navController, appBarConfiguration)
I've met a problem with the back stack while starting the app from a deep link (via notification).
The general idea is that I have 3 fragments (A, B, C). The A screen works as splash screen, so I've put a popUpTo and popUpToInclusive to the action, so I can never see back to it again. My deep link destination is fragment C. To achieve the proper back stack I've merged B and C into nested navigation. Unfortunately, when I press Back button in fragment B the app is showing fragment A. My guess this is because the action from A to B with popUpTo was never called when the app was started from the deep link itself. My question is: how to avoid going back from B to A in this particular scenario?
Thanks in advance! I attach the sample code:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/A"
android:name="A"
android:label="A" >
<action
android:id="#+id/action_A_to_B"
app:destination="#id/B"
app:popUpTo="#id/A"
app:popUpToInclusive="true" />
</fragment>
<navigation android:id="#+id/nested_nav"
app:startDestination="#id/B">
<fragment
android:id="#+id/B"
android:name="B"
android:label="B">
<action
android:id="#+id/action_B_to_C"
app:destination="#id/C" />
</fragment>
<fragment
android:id="#+id/C"
android:name="C"
android:label="C">
</fragment>
</navigation>
</navigation>
And my DeepLinkBuilder (nothing special, same as the docs)
NavDeepLinkBuilder(requireContext())
.setGraph(R.navigation.nav_graph)
.setDestination(R.id.userProfileFragment)
.createPendingIntent()
.send()
You have set the navGraph that contain the splash screen as the default startDestination which lead to show it when you press back button from the nested navGraph , I think the easiest solution is to do the following :
First edit you nav file to have only one navigation like :
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/A"
android:name="A"
android:label="A" >
<action
android:id="#+id/action_A_to_B"
app:destination="#id/B"
app:popUpTo="#id/A"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/B"
android:name="B"
android:label="B">
<action
android:id="#+id/action_B_to_C"
app:destination="#id/C" />
</fragment>
<fragment
android:id="#+id/C"
android:name="C"
android:label="C">
</fragment>
</navigation>
Secondly set a listener to navController to listen when a destination change occur , like this :
yourNavController.setOnDestinationChangedListener(this);
override onDestinationChangedListener() method and check if the fragment which about to get showed is A , then finish the app like :
#override
public void onDestinationChanged(NavController controller, NavDestination destination, Bundle arguments){
if(destination.getId()==R.id.splashFragmentId){
finish();
}
}
I am using Navigation Component to build my application. I would like to implement SingleTask launch mode in the fragment navigation how can I achieve it?
For more clarity, I will detail my problem.
I have a HomeFragement(Lets Fragment A) and from that I have defined actions to the inner screens
Fragment A > Fragment B > Fragment C > Fragment D
I have a BaseFragment which extends all the above fragments in which I have implemented back click and its action using findNavController().popBackStack()
In Fragment D when the user clicks the back button as expected, it is navigating back to Fragment C. My problem comes when I have called an action whose destination fragment is Fragment A. I am calling this action on a successful event in Fragment D, that action also works well. But when the user press the back button from Fragment A it goes to Fragment C, the next back click goes to Fragment B, then the next back click to FRagment A. I should destroy Fragment B and Fragment C on the successful event called in Fragment D and should resume Fragment A.
I know this flow can be achieved by using launch mode as SingleTask of the first screen(Fragment A) if it is an Activity instead of Fragment.
My navigation graph XML file
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/app_navigation"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="FragmentA"
tools:layout="#layout/fragment_a">
<action
android:id="#+id/action_FragmentA_to_FragmentB"
app:destination="#id/dashboardFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentB"
android:name="FragmentB"
tools:layout="#layout/fragment_b">
<action
android:id="#+id/action_FragmentB_to_FragmentC"
app:destination="#id/dashboardFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentC"
android:name="FragmentC"
tools:layout="#layout/fragment_c">
<action
android:id="#+id/action_fragemntC_to_fragementA"
app:launchSingleTop="true"
app:popUpToInclusive="true"
app:destination="#id/deliveriesFragment" />
</fragment>
</navigation>
But when the user press the back button from Fragment A it goes to Fragment C, the next back click goes to Fragment B, then the next back click to FRagment A.
If I understand correctly, your Fragment A is the start destination, like a main page, and you want the behavior of "whenever I come back to Fragment A, the next back button I click should exit the app", right?
If so, try to add action from any Fragment to Fragment A and set:
app:popUpTo="#id/fragmentA"
app:popUpToInclusive="true" />
It should look like this:
Demo: https://youtu.be/LNyk_FEkZoA
Posting my working solution, the full navigation script which includes the answer of #Sam Chen
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/app_navigation"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="FragmentA"
tools:layout="#layout/fragment_a">
<action
android:id="#+id/action_FragmentA_to_FragmentB"
app:destination="#id/dashboardFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentB"
android:name="FragmentB"
tools:layout="#layout/fragment_b">
<action
android:id="#+id/action_FragmentB_to_FragmentC"
app:destination="#id/dashboardFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentC"
android:name="FragmentC"
tools:layout="#layout/fragment_c">
<action
android:id="#+id/action_fragemntC_to_fragementA"
app:popUpToInclusive="true"
app:popUpTo="#id/fragmentA" />
</fragment>
</navigation>
I have navigated to a DialogFragment from Navigation drawer that connected to NavController. But when I navigate to another destination which I have set popUpTo and Inclusive of Dialog fragment it does not clear stack. How can I clear stack?
AM calling this method from LogoutDialog
findNavController().navigate(R.id.action_logoutDialog_to_auth_navigation)
Am using fragments and only one activity.
This is part of my navigation graph
<fragment
android:id="#+id/homeFragment"
android:name="io.ui.home.HomeFragment"
tools:layout="#layout/fragment_home" >
<action
android:id="#+id/action_homeFragment_to_auth_navigation"
app:destination="#id/auth_navigation"
app:popUpTo="#id/homeFragment"
app:popUpToInclusive="true" />
<argument
android:name="fromLogin"
app:argType="boolean"
android:defaultValue="false" />
</fragment>
<dialog
android:id="#+id/logoutDialog"
android:name="io.ui.dialog.LogoutDialog"
android:label="LogoutDialog" >
<action
android:id="#+id/action_logoutDialog_to_auth_navigation"
app:destination="#id/auth_navigation"
app:popUpTo="#id/logoutDialog"
app:popUpToInclusive="true" />
</dialog>
<!-- auth navigation graph -->
<navigation
android:id="#+id/auth_navigation"
app:startDestination="#id/loginFragment">
<fragment
android:id="#+id/loginFragment"
android:name="io.ui.login.LoginFragment"
android:label="fragment_login"
tools:layout="#layout/fragment_login">
<action
android:id="#+id/action_loginFragment_to_OTPFragment"
app:destination="#id/OTPFragment"
app:popUpTo="#id/loginFragment"
app:popUpToInclusive="true" />
<action
android:id="#+id/action_loginFragment_to_homeFragment"
app:destination="#id/homeFragment" />
<action
android:id="#+id/action_loginFragment_to_resetPasswordFragment"
app:destination="#id/resetPasswordFragment" />
</fragment>
<fragment
android:id="#+id/OTPFragment"
android:name="io.ui.login.OTPFragment"
android:label="fragment_otp"
tools:layout="#layout/fragment_otp">
<action
android:id="#+id/action_OTPFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#id/OTPFragment"
app:popUpToInclusive="true" />
<action
android:id="#+id/action_OTPFragment_to_loginFragment"
app:destination="#id/loginFragment"
app:popUpTo="#id/OTPFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/resetPasswordFragment"
android:name="io.ui.profile.PasswordFragment"
android:label="fragment_reset_password"
tools:layout="#layout/fragment_password" >
<argument
android:name="isReset"
app:argType="boolean"
android:defaultValue="false" />
</fragment>
</navigation>
You're a looking for these attribute to put in your action.
The popUpTo attribute of an action "pops up" the back stack to a given destination before navigating. (Destinations are removed from the back stack.)
If the popUpToInclusive attribute is false or is not set, popUpTo removes destinations up to the specified destination, but leaves the specified destination in the back stack.
If popUpToInclusive is set to true, the popUpTo attribute removes all destinations up to and including the given destination from the back stack.
If popUpToInclusive is true and popUpTo is set to the app's starting location, the action removes all app destinations from the back stack. The Back button takes the user all the way out of the app.
EDIT: ALSO I'm adding this really great video which explains it with example https://www.youtube.com/watch?v=mLfWvSGG5c8