androidanimationnavigationandroid-jetpack-composecomposable

Get previous page in jetpack composable navigation on Android to manage page enter/exit animations


I would like to have different navigation animations for the same page, but for different navigation scenarios. E.g.

Practical scenario is e.g. page1 is one of the page from bottomNavigationBar. page5 is a page to update some properties from the page1. I want to highlite by using different navigation animations that page1 is the main one, and page5 is the secondary.

So I faced with the following issue. I cannot find robust solution to get "previous" and "next" page names. The code bock below is the enterTransition for page1 with condition operator to support different animations based on previous page name.

enterTransition = { //enterTransition for page1
                when("previousPageName") {
                    "page1" ->   fadeIn(animationSpec = tween(delayMillis = 250)) + slideInHorizontally(initialOffsetX = {-it}, animationSpec = tween(delayMillis = 150, durationMillis = 200))
                    "page2" ->  fadeIn() + slideInHorizontally()
                    else ->  fadeIn()
                }

            }

to get previous page name, apparently, the best way is to use NavigationController, so the sample above can be rewritten.

enterTransition = {  //enterTransition for page1
                when(**navController.previousBackStackEntry!!.destination.route**) {
                    "page1" ->   fadeIn(animationSpec = tween(delayMillis = 250)) + slideInHorizontally(initialOffsetX = {-it}, animationSpec = tween(delayMillis = 150, durationMillis = 200))
                    "page2" ->  fadeIn() + slideInHorizontally()
                    else ->  fadeIn()
                }

            }

I've expected to find name of previous page there anytime. But practically, in the case when navigation is done with navigationController.popUpBackStack() e.g. clicking on "return back" button. It removes/breaks navController.previousBackStackEntry and as a result it contains a page I do not expect e.g. "page3". And I can understand such behavior, it operates with BackStack, but if so, I have no idea how to manage different animations between different pages based on a navigation graph state. One possible solution could be to have/manage a variable to store previous page, but that is the last I will use to achieve my goals. It would be nice to find more elegant solution.

So I have a couple questions:

  1. Is there any common solution to have different enter/exit animations for one page based on a navigation scenario?
  2. What is the best way to get previous page and do not worry about popUpBackStack() or
navController.navigate("page1") {
                            popUpTo(
                                "page5"
                            ) { inclusive = true }
                        }

Thank you in advance for help.

Tried to get previous page from navigationController.previousBackStackEntry but without needed result


Solution

  • As explained in the Animations in Navigation Compose blog post, each transition lambda is in the format of:

    enterTransition: (
        AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?
    )? = null,
    

    Which, as it explains:

    That lambda uses the AnimatedContentScope to provide you with the NavBackStackEntry of where you are coming from (the initialState) and where you are going to (the targetState). For example, for the enterTransition, the entering destination is the targetState — the one you are applying the enterTransition to.

    Which lets you write your code such as:

    enterTransition = {
        // initialState is the screen you are coming from (the one leaving)
        // targetState is the screen you are going to (the one entering)
        when(initialState.destination.route) {
            "page1" ->   fadeIn(animationSpec = tween(delayMillis = 250)) + slideInHorizontally(initialOffsetX = {-it}, animationSpec = tween(delayMillis = 150, durationMillis = 200))
            "page2" ->  fadeIn() + slideInHorizontally()
            else ->  fadeIn()
        }
    }