Android NavigationUI - Handle backpress when starting fragment is poped out - android

I am using NavigationUI of android architecture components with bottom navigation in my android app. I have the following situation
The app starts at Fragment A
Initial calculation is done and now I pop out Fragment A by navController.popBackStack()
The app goes to Fragment B (This serves as a 'Home' for the app)
Now there are more fragments say C and D
The user can navigate between B, C and D
Now the problem is in my navigation graph, my starting fragment was A (by defining app:startDestination="#id/fragmentA") but that is now pop out of the activity. Due to this, anytime if I backpress, the app just closes instead going back to the previous fragment. For example, let's say I navigate to Fragment C or D from fragment B and if I press back button, my app will close instead of going to fragment B. If the there way to 'reassign' the startDestination?
I checked the answer to this question which has a similar situation as above but using NavOptions is somehow not working. Is there any other way? Following is the code I used to pop fragment and add nav options
navController.popBackStack()
val navOptions: NavOptions = NavOptions.Builder()
.setPopUpTo(R.id.homeFragment, true)
.build()
navController.navigate(R.id.action_fragmentA_to_fragmentB, null, navOptions)

Use this navigation graph. Hope it will solve your problem.
<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/fragment_a">
<fragment
android:id="#+id/fragment_a"
tools:layout="#layout/fragment_a">
<action
android:id="#+id/action_a_to_b"
app:destination="#id/fragment_b"
app:popUpTo="#id/fragment_a"
app:popUpToInclusive="true"/>
</fragment>
<fragment
android:id="#+id/fragment_b"
tools:layout="#layout/fragment_b">
<action
android:id="#+id/action_b_to_c"
app:destination="#id/fragment_c"/>
</fragment>
<fragment
android:id="#+id/fragment_c"
tools:layout="#layout/fragment_c">
</fragment>
</navigation>
Use the following line for navigation:
navController.navigate(R.id.action_a_to_b)
navController.navigate(R.id.action_b_to_c)

Related

how to remove startDestination when creating deepLink with NavDeepLinkBuilder?

imagine I have navGraph like this with startDestination="#id/rootFragment" :
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/nav_graph"
app:startDestination="#id/rootFragment">
<fragment
android:id="#+id/rootFragment"
android:name="com.nima.RootFragment"
android:label="rootFragment">
//some actions...
</fragment>
//other fragments...
</navigation>
and I want to create a Deeplink like this:
NavDeepLinkBuilder(context).
addDestination(R.id.FragmentA).
addDestination(R.id.FragmentB).
setGraph(R.navigation.nav_graph).
createPendingIntent()
so I want to show FragmentA first and FragmentB next, and when the user hits the back button only show the FragmentA and not showing the startDestination(rootFragment).
by default, the startDestination is in backStack, my question is how can I remove it?
NavDeepLinkBuilder has no method like PopUpto to remove it.

How to navigate to fragment with arguments without recreating it?

I have a question. I'm using nav component for navigation. For example i have fragment A, B and C and bottomNavigation. I'm using
binding.bottomNavigation.setupWithNavController(navController)
For multiple backstack. But here is situation: Main frag is A. I'm moving to fragment B or C. I have buttons on fragments B and C which should lead me to fragment A with putted arguments in it so i'm using just:
findNavController().navigate(fragmentBDirections.fromFragmentBToFragmentA(argument))
But here is a problem. I'm recreating fragment A after this but i'm already have this fragment in backstack. So is it possible to find A in backstack and navigate to it without recreating? Is it possible to save backstack after that?
Your problem seems like an ideal case to use a sharedViewModel
Your button in B or C should pop and fallback to A after updating a property in the viewModel. On leaving, the viewModel is not destroyed because it is bound to the activity and is available for Fragment A.
Bonus is using LiveData so that the change is observed and updated automatically
I think SavedStateHandle might be helpful.
<Navigation>
<fragment
android:id="#+id/BFragment"
android:name="com.packageName.app.BFragment"
android:label="fragment_b"
tools:layout="#layout/fragment_b" >
<action
android:id="#+id/action_BFragment_pop_including_AFragment"
app:popUpTo="#id/AFragment"
app:launchSingleTop="true" />
</Navigation>
<Navigation>
<fragment
android:id="#+id/CFragment"
android:name="com.packageName.app.CFragment"
android:label="fragment_c"
tools:layout="#layout/fragment_c" >
<action
android:id="#+id/action_CFragment_pop_including_AFragment"
app:popUpTo="#id/AFragment"
app:launchSingleTop="true" />
</Navigation>
try it . it's my solution

How can I go back directly to Fragment A since Fragment D without going back to Fragment C?

I use Android Navigation Architecture Component.
I have three Fragment A, B and C
Fragment A -> Fragment B -> Fragment D
Fragment A -> Fragment C -> Fragment D
On fragment D I have a button used to validate modifications and finish.
From this Frgament D I would like to:
return to Fragment B when I come from Fragment B (I use findNavController().navigateUp())
come back to Fragment A when I come from Fragment C (I don't know how to do it)
How can I go back directly to Fragment A when I come from Fragment D without going back to Fragment C?
Should I wait for the return on Fragment C and reprocess for the execution of findNavController().NavigateUp().
I use setPopUpTo on NavOptions, or you can use it inside the xml as well
documentation => https://developer.android.com/reference/androidx/navigation/NavOptions.Builder#setPopUpTo(int,%20boolean)
You can achieve both of these action with findNavController().NavigateUp()
Given the following graph for example:
<?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/test_nav_graph"
app:startDestination="#id/a">
<fragment
android:id="#+id/a"
android:name="fragment.A"
tools:layout="#layout/a_fragment"
android:label="A" >
<action
android:id="#+id/action_a_to_b"
app:destination="#id/b" />
<action
android:id="#+id/action_a_to_c"
app:destination="#id/c" />
</fragment>
<fragment
android:id="#+id/b"
android:name="fragment.B"
tools:layout="#layout/b_fragment"
android:label="B" >
<action
android:id="#+id/action_b_to_d"
app:destination="#id/d" />
</fragment>
<fragment
android:id="#+id/c"
tools:layout="#layout/c_fragment"
android:name="fragment.C"
android:label="C" >
<action
android:id="#+id/action_c_to_d"
app:destination="#id/d"
app:popUpTo="#id/a"/>
</fragment>
<fragment
android:id="#+id/d"
tools:layout="#layout/d_fragment"
android:name="fragment.D"
android:label="D" />
</navigation>
When you take the path: A->B->D your back stack is A,B,D and when you call NavigateUp() you go back to B.
Now the path A->C->D has a minor addition in the action tag as you can see
<action
android:id="#+id/action_c_to_d"
app:destination="#id/d"
app:popUpTo="#id/a"/>
it has popUpTo, so when you navigate from C to D app:popUpTo tells the Navigation library to pop some destinations off of the back stack as part of the call to navigate(), so after navigating to D your back stack is A,D and NavigateUp() takes you back to A
So when you are in Fragment D and pressing back button . You can check the fragment stack and check for example
onBackPress(){
childFragmentManager.popbackstack()
val currentFragment = findFragmentByTag("FRAGMENT_B")
if(currentFragment == childFragmentManager.primaryNavigation()){
childFragmentManager.popbackstack()
}
NOTE
Above snippet is written without editor. You can use this logic.

How to pop through fragment stack in NavController without loosing "parent" state

I open some main fragment FragA, which then can open other fragments, that are added to the stack, but when I press back I show a DialogC, which should clear the stack and get me back to FragA, without loosing it's state, restore it from stack, rather then creating it - to recreate it I'll have to pass some arg through the whole stack.
I tried some configs with popUpTo in different places, and also used findNacController.popUpTo(with/out_aruments) or findNavController.navigate(R.id.action_dialog_c_to_frag_a) without destination defined in action, but pop can't find action in stack, navigate wants to recrete fragment when destination is defined, withou it cannot find pop action in stack (I/NavController: Ignoring popBackStack to destination frag_a as it was not found on the current back stack)
This is sample of my nav_graph:
<?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">
<fragment
android:id="#+id/asdf"
android:name="SomeNaviFragment">
<action
android:id="#+id/action_asdf_to_frag_a"
app:destination="#id/frag_A" />
</fragment>
<fragment
android:id="#+id/frag_A"
android:name="FragA">
<argument
android:name="some_id"
app:argType="integer" />
<action
android:id="#+id/action_frag_a_to_frag_b"
app:destination="#id/frag_B" />
</fragment>
<fragment
android:id="#+id/frag_B"
android:name="FragB">
<action
android:id="#+id/action_frag_b_to_dialog_frag_c"
app:destination="#id/DialogFragC"/>
</fragment>
<dialog
android:id="#+id/DialogFragC"
android:name="DialogC">
<action
android:id="#+id/action_dialog_c_to_frag_a"
app:popUpTo="#id/frag_a"
app:popUpToInclusive="true"/>
</dialog>
</navigation>
In short - I wan't to go deeper from FragA through some fragments, but when the DialogC shows up in some point, I want to get back to FragA, to it's initial state. Is it possible to achive it without passing the creation arguments for FragA?
Some solution already tried, like: Navigate Back with Navigation Component with it's linked resources, but this didn't help at all.
I've used the article in your post to make this https://github.com/yoobi/backNavigation I hope it helps.
EDIT: you're looking for a combination of this and the article then.
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.
You can also check the count of your backstack with : parentFragmentManager.backStackEntryCount

Is it possible to start with a "non-start" fragment using Android Navigation Architecture Component(Android Jetpack)?

so I have the following navigation graph:
Fragment A (start) --> Fragment B
So for some situations (firebase notifications), I need to start Fragment B directly, passing data from the notifications. Now, this works. However, when I press the back button, it results in a crash. Is it because the leading fragment (Fragment A) is not in the stack? If so, is there a way to properly handle this.
Basically, I need the backPressed action to launch the start Fragment (Fragment A) in a situation where Fragment B is launched directly without passing through Fragment A.
Below is a snippet of my graph:
<fragment
android:id="#+id/homeFragment"
android:name="dita.dev.myportal.ui.home.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/action_homeFragment_to_messageDetailFragment"
app:destination="#id/messageDetailFragment"
app:exitAnim="#anim/fade_out_animation" />
</fragment>
<fragment
android:id="#+id/messageDetailFragment"
android:name="dita.dev.myportal.ui.messages.details.MessageDetailFragment"
android:label="Message"
tools:layout="#layout/fragment_message_detail">
<argument
android:name="title"
app:argType="string" />
<argument
android:name="message"
app:argType="string" />
</fragment>
I hope the link helps.
https://developer.android.com/guide/navigation/navigation-deep-link
Deep links allow for synthetic back-stacks.
Quoted from the website, "This means that when a user presses the Back button from a deep link destination, they navigate back up the navigation stack just as though they entered your app from its entry point."
Maybe trying to create a deep link for Fragment B will work.

Categories

Resources