Search code examples
androidkotlinandroid-jetpack-composenavhostcontroller

How to apply NavHost custom transition animation in jetpack compose android app


I'm implementing the new model of the Android NavHost navigation system. However, I can not resolve how to apply the custom transition animation,

Here's my implemented navhost,

AndroidNavigationTheme {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = HomeScreen,
    ){
        composable<HomeScreen>{
            HomeScreen(
                onClickGoAboutScreen = {
                    navController.navigate(AboutScreen)
                }
            )
        }

        composable<AboutScreen>{
            AboutScreen(
                onClickGoContentScreen = {
                    navController.navigate(ContentScreen)
                }
            )
        }

        composable<ContentScreen>{
            ContentScreen(
                onClickGoHomeScreen = {
                    navController.navigate(HomeScreen)
                }
            )
        }
    }

}


@Serializable
object HomeScreen

@Serializable
object AboutScreen

@Serializable
object ContentScreen

And here's my code for the transition animation,

fun scaleIntoContainer(
    direction: ScaleTransitionDirection = ScaleTransitionDirection.INWARDS,
    initialScale: Float = if (direction == ScaleTransitionDirection.OUTWARDS) 0.9f else 1.1f
): EnterTransition {
    return scaleIn(
        animationSpec = tween(220, delayMillis = 90),
        initialScale = initialScale
    ) + fadeIn(animationSpec = tween(220, delayMillis = 90))
}

fun scaleOutOfContainer(
    direction: ScaleTransitionDirection = ScaleTransitionDirection.OUTWARDS,
    targetScale: Float = if (direction == ScaleTransitionDirection.INWARDS) 0.9f else 1.1f
): ExitTransition {
    return scaleOut(
        animationSpec = tween(
            durationMillis = 220,
            delayMillis = 90
        ), targetScale = targetScale
    ) + fadeOut(tween(delayMillis = 90))
}

enum class ScaleTransitionDirection {
    INWARDS,
    OUTWARDS
}

I'm not too fond of the default transition that the Navhost provided, If possible also suggest some good transition animation too.


Solution

  • The new typesafe composable function still offers parameters for custom transitions:

    inline fun <T : Any> NavGraphBuilder.composable(
        typeMap: Map<KType, NavType<*>> = emptyMap(),
        deepLinks: List<NavDeepLink> = emptyList(),
        noinline enterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition)? = null,
        noinline exitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition)? = null,
        noinline popEnterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition)? = enterTransition,
        noinline popExitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition)? = exitTransition,
        noinline sizeTransform: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform)? = null,
        noinline content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit
    ): Unit
    

    You can simply apply them as follows:

    composable<HomeScreen>(
        enterTransition = {
            scaleIntoContainer()
        },
        exitTransition = {
            scaleOutOfContainer()
        }
    ) {
        HomeScreen(
            onClickGoAboutScreen = {
                navController.navigate(AboutScreen)
            }
        )
    }
    

    If the animation does not occur in your case, please double-check your animation and how you are calling it. I just verified with these two simple animations that it works on androidx.navigation:navigation-compose:2.8.1:

    fun scaleIntoContainer(): EnterTransition {
        return slideInHorizontally(
            animationSpec = tween(2000),
            initialOffsetX = { fullWidth -> fullWidth }
        )
    }
    
    fun scaleOutOfContainer(): ExitTransition {
        return slideOutHorizontally(
            animationSpec = tween(2000),
            targetOffsetX = { fullWidth -> -fullWidth }
        )
    }