Compose Navigation에서 Screen Transition 설정하기

지프치프·2025년 6월 3일
0

Android

목록 보기
94/94
post-thumbnail

“Android 로봇은 Google에서 제작하여 공유한 저작물을 복제하거나 수정한 것으로 Creative Commons 3.0 저작자 표시 라이선스의 약관에 따라 사용되었습니다.”


개요

Compose에서 각 Screen(Compsoable)이 전환될 때 Transition을 설정하고 싶을 때가 있다. Navigation을 사용 중이라면 NavHost에서 손쉽게 설정이 가능하다.

아래의 예제소스는 MainScreen을 시작으로
FirstScreen, SecondScreen으로 전환하는 소스이다.

@Composable
fun AppNavHost(
    navHostController: NavHostController,
    paddingValues: PaddingValues,
    viewModel: MainViewModel,
    stateViewModel: StateViewModel
) {
    NavHost(
            navController = navHostController,
            startDestination = Screen.Main.route,
            enterTransition = {
                slideIntoContainer(
                    AnimatedContentTransitionScope.SlideDirection.Left,
                    animationSpec = tween(300)
                )
            },
            exitTransition = {
                slideOutOfContainer(
                    AnimatedContentTransitionScope.SlideDirection.Left,
                    animationSpec = tween(300)
                )
            },
            popEnterTransition = {
                slideIntoContainer(
                    AnimatedContentTransitionScope.SlideDirection.Right,
                    animationSpec = tween(300)
                )
            },
            popExitTransition = {
                slideOutOfContainer(
                    AnimatedContentTransitionScope.SlideDirection.Right,
                    animationSpec = tween(300)
                )
            }
        ) {
            composable(Screen.Main.route) {
                MainScreen(navHostController)
            }
            composable(Screen.First.route) {
                FirstScreen(viewModel, stateViewModel)
            }
            composable(Screen.Second.route) {
                SecondScreen(viewModel, stateViewModel)
            }
        }
}

예를 들어 위와 같은 소스가 있다고 가정해보면
NavHost 의 파라미터를 지정하는 것으로 간단하게 Transition을 설정할 수 있다.

각 파라미터의 개념을 MainScreen에서 FirstScreen으로 전환된다는 가정 하에 아래와 같이 설명할 수 있다.

  • enterTransition FirstScreen으로 진입 시 애니메이션
  • exitTransition FirstScreen으로 진입할 때 MainScreen의 애니메이션
  • popEnterTransition 뒤로가기 시 MainScreen의 애니메이션
  • popExitTransition 뒤로가기 시 FirstScreen의 애니메이션
  • slideIntoContainer 다음 Screen 진입 시 사용될 slide 애니메이션 객체, EnterTransition 을 반환한다.
  • slideOutOfcontainer 이전 Screen으로 돌아갈 때 사용될 slide 애니메이션 객체, ExitTransition 을 반환한다.

composable에서 설정

NavHost가 아닌 composable에서 설정도 가능하다.

모든 Screen에 각 각 다른 transition을 설정하고 싶은 경우에 해당한다.

NavHost와 마찬가지로 파라미터로 설정할 수 있다.

			composable(
                route = Screen.First.route,
                enterTransition = {
                    slideIntoContainer(
                        AnimatedContentTransitionScope.SlideDirection.Left,
                        animationSpec = tween(300)
                    )
                },
                exitTransition = {
                    slideOutOfContainer(
                        AnimatedContentTransitionScope.SlideDirection.Left,
                        animationSpec = tween(300)
                    )
                },
                popEnterTransition = {
                    slideIntoContainer(
                        AnimatedContentTransitionScope.SlideDirection.Right,
                        animationSpec = tween(300)
                    )
                },
                popExitTransition = {
                    slideOutOfContainer(
                        AnimatedContentTransitionScope.SlideDirection.Right,
                        animationSpec = tween(300)
                    )
                }
            ) {
                FirstScreen(viewModel, stateViewModel)
            }

NavHost 예제와 마찬가지로 MainScreen, FirstScreen, SecondScreen이 있다고 가정해보자,
위 예제소스대로 작동해보면 FirstScreen의 transition만 적용되는 것을 확인할 수 있다

모든 Screen에 transition을 적용하고 특정 Screen만 transition을 다르게 하고 싶을 수도 있다.
이럴 때 NavHost에 transition을 지정 후 특정 Screen composable에만 transition을 다르게 적용하면 되나 싶지만
이럴 경우 transition이 중복되어 의도한 바와 전혀 다른 결과물이 나온다.

특정 Screen만 다른 transition을 적용할 때

이때에는 NavHost 의 transition 파라미터 분기에서 현재 route를 기준으로 분기문을 작성하여 처리하면 된다.
아래 예제를 살펴보지

@Composable
fun AppNavHost(
    navHostController: NavHostController,
    paddingValues: PaddingValues,
    viewModel: MainViewModel,
    stateViewModel: StateViewModel
) {
    NavHost(
            navController = navHostController,
            startDestination = Screen.Main.route,
            enterTransition = {
                when(targetState.destination.route) {
                    Screen.First.route -> EnterTransition.None
                    Screen.Second.route -> {
                        fadeIn(animationSpec = tween(300))
                    }
                    else -> {
                        slideIntoContainer(
                            AnimatedContentTransitionScope.SlideDirection.Left,
                            animationSpec = tween(300)
                        )
                    }
                }
            },
            exitTransition = {
                when(targetState.destination.route) {
                    Screen.First.route -> ExitTransition.None
                    Screen.Second.route -> {
                        fadeOut(animationSpec = tween(300))
                    }
                    else -> {
                        slideOutOfContainer(
                            AnimatedContentTransitionScope.SlideDirection.Left,
                            animationSpec = tween(300)
                        )
                    }
                }
            },
            popEnterTransition = {
                when(targetState.destination.route) {
                    Screen.First.route -> EnterTransition.None
                    Screen.Second.route -> {
                        fadeIn(animationSpec = tween(300))
                    }
                    else -> {
                        slideIntoContainer(
                            AnimatedContentTransitionScope.SlideDirection.Right,
                            animationSpec = tween(300)
                        )
                    }
                }
            },
            popExitTransition = {
                when(targetState.destination.route) {
                    Screen.First.route -> ExitTransition.None
                    Screen.Second.route -> {
                        fadeOut(animationSpec = tween(300))
                    }
                    else -> {
                        slideOutOfContainer(
                            AnimatedContentTransitionScope.SlideDirection.Right,
                            animationSpec = tween(300)
                        )
                    }
                }
            }
        ) {
            composable(Screen.Main.route) {
                MainScreen(navHostController)
            }
            composable(Screen.First.route) {
                FirstScreen(viewModel, stateViewModel)
            }
            composable(Screen.Second.route) {
                SecondScreen(viewModel, stateViewModel)
            }
        }
}

분기문을 살펴보면 targetState는 이동(전환)하려는 Screen의 BackStackEntry으로 route를 이용한 분기처리가 가능하다.

추가적으로 transitoin 애니메이션은 slide, fade, scale 등 다양한 방법을 지원하며 이 부분에 대해서는 추후 다시 포스팅을 작성해보도록 하겠다.

개인적으로 공부했던 것을 바탕으로 작성하다보니
잘못된 정보가 있을수도 있습니다.
인지하게 되면 추후 수정하겠습니다.
피드백은 언제나 환영합니다.
읽어주셔서 감사합니다.

profile
지프처럼 거침없는 개발을 하고싶은 개발자

0개의 댓글