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>
Related
I need reload fragment after this process
Go from Fragment A to Fragment B and go from Fragment B to Fragment C, then do the registration process, and after the registration is correct, popBackStack Fragment A and my Fragment A will be reloaded.
My main problem is that Fragment A is not reloaded
Please help me
you can use navigation component library for solve this problem!
this will be your nav_graph.xml
<?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"
app:startDestination="#id/AFragment">
<fragment
android:id="#+id/AFragment"
android:name="ir.inbo.navigationComponenetBug.AFragment"
android:label="AFragment"
tools:layout="#layout/fragment_a">
<argument
android:name="someLong"
app:argType="long" />
<action
android:id="#+id/action_AFragment_to_BFragment"
app:destination="#id/BFragment" />
</fragment>
<fragment
android:id="#+id/BFragment"
android:name="ir.inbo.navigationComponenetBug.BFragment"
android:label="BFragment"
tools:layout="#layout/fragment_b">
<argument
android:name="someLong"
app:argType="long" />
<action
android:id="#+id/action_BFragment_to_CFragment"
app:destination="#id/CFragment" />
</fragment>
<fragment
android:id="#+id/CFragment"
android:name="ir.inbo.navigationComponenetBug.CFragment"
android:label="CFragment"
tools:layout="#layout/fragment_c" >
<argument
android:name="someLong"
app:argType="long" />
<action
android:id="#+id/action_CFragment_to_AFragment"
app:destination="#id/AFragment"
app:popUpTo="#id/AFragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>
and in AFragment you must move your logic to onViewCreated or onCreateView because of the fragment life cycle when you navigate from AFragment to Bfragment, Afragment's view will be destroyed and when you come back to Afragment from Cfragment, onViewCreated and onViewCreate will be called
and this is google example for your exact use case
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 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.