Hi all! I have a navigation component and I am won...
# android
j
Hi all! I have a navigation component and I am wondering about transitions while I am changing graphs. The case: I have a graph, which shows a start screen, a login flow and a logged in graph:
Copy code
<MainNavGraph>
    <StartFragment>
    <LoginFlowGraph>
         <UsernameFragment>
         <PasswordFragment>
    </LoginFlowGraph>
    <LoggedInGraph>
         <HomeFragment>
    </LoggedInGraph>
</MainNavGraph>
My problem is this: when I am logged in, I want to go to the LoggedInGraph and pop everything what is on the backstack:
Copy code
if (!navController.currentDestination.isPartOfGraph(R.id.loggedInNavGraph)) {
	navController.popBackStack(R.id.mainNavGraph, false)
	navController.navigate(R.id.loggedInNavGraph)
}
This works fine. The graph is basically emptied to the root and then the loggedInNavGraph starts. I want to use material transitions during this, On the login flow I use Material shared transition on the X axis
Copy code
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
On the HomeFragment I use transitions on the Z axis:
Copy code
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
However, popping the backstack after the login flow, shows the fragments in the backstack also with the wrong animation... Is it possible to clear the backstack without the fragments from within the backstack to show themselves during the transition?
Here is the video. I have some issues recording from an emulator, so if you ignore the black artifacts you should see the UsernameFragmetn pop up after pushing the login button
animation_overlap_backstack_naventries.webm
okay I "solved" it. Apparently popping to the beginning of the nav graph makes the first fragment appear again. So added a backstack listener, removed transitions and the backstack listener which seems to have solved it
Copy code
clearAnimationsForNavBackStack()
navController.popBackStack(R.id.mainNavGraph, true)
navController.navigate(R.id.loggedInNavGraph)
Copy code
private fun clearAnimationsForNavBackStack() {
		val navigationFragmentManager = navigationFragmentManager ?: return
		if (navigationFragmentManager.backStackEntryCount > 0) {
			navigationFragmentManager.addOnBackStackChangedListener(object : FragmentManager.OnBackStackChangedListener {
				override fun onBackStackChanged() {
					currentNavigationFragment?.apply {
						enterTransition = null
						exitTransition = null
						reenterTransition = null
						returnTransition = null
					}
					navigationFragmentManager.removeOnBackStackChangedListener(this)
				}
			})
		}
	}
i
This entirely the wrong way to do login with Navigation. Take a look at the recent Navigating Navigation video (

https://youtu.be/09qjn706ITA

) and the documentation (https://developer.android.com/guide/navigation/navigation-conditional)
j
Hmm makes sense. I guess I can control this as well from my activity with an ondestinationChanged listener to launch the login flow when necessary. The example is only talking about 1 login fragment, so I am curious what happens if I pop 2 or 3 fragments at the same time (because my login flow might have 2 or 3 fragments). Maybe in that case I will still have issues with all animations in the stack popping/showing
i
Fragments, and by extension Navigation, certainly do support transitions when popping multiple fragments simultaneously
j
And how exactly?
Like my problem is I set the enter/exit/return/reenter transitions and they go off at the wrong moment
but perhaps with this different graph its better
@Ian Lake can you perhaps still elaborate on your last comment? If I have my graph like this:
Copy code
<MainNavGraph start=LoggedInGraph>
    <LoginGaph>
        <WelcomeFragment/>
        <UsernameFragment/>
        <PasswordFragment/>
	</LoginGraph>
	<LoggedInGraph>
        <HomeFragment/>
	</LoggedInGraph>
</MainNavGraph>
I start up the app by deciding, did I log in yes or no, if not I open the LoginGraph on top of whatever destination I am on. That is according as what your article linked. As you can see, my login flow consists of several screens. Once I finish the login, I pop to the LoginGraph with that destination inclusive and I end up in my logged in graph. HOWEVER: The PasswordFragment is the fragment that should be disappearing, but the WelcomeFragment is somehow showing up as well (and NOT the UsernameFragment), resulting in 2 fragments running their onReturnTransition. It doesn't matter how many fragments I have in the stack that I pop, it is always the first in the stack that does an unwanted appearance. Do you know why this is happening and can it be avoided? As you can imagine, when I am at the end of my LoginGraph and pop the entire LoginGraph, I of course only want to see the last destination transitioning out.
okay perhaps it had to do with me putting in this code: https://github.com/android/architecture-components-samples/issues/495
i
There's a number of important fixes around postponed transitions in the latest Fragment 1.3 alphas (it is Fragments that control the transitions) as per our blog post: https://link.medium.com/KpNBVy4flab
It certainly sounds like only some of your fragments are postponed, leading to those types of issues. The new state manager fixes that whole class of problems
j
@Ian Lake thanks for your answer! I appreciate it, will try it out
i
The other option is to just make sure all of your fragments postpone, even if they don't have to (you can use
postponeEnterTransition(0, TimeUnit.MILLISECONDS)
to just postpone for one frame, no
startPostponedEnterTransition()
needed)