Animar entre destinos

O NavDisplay oferece recursos de animação integrados para criar transições visuais suaves à medida que os usuários navegam pelo app. É possível personalizar essas animações globalmente para o NavDisplay ou no nível do Scene usando metadados.

Entender os recursos de animação integrados

O NavDisplay usa a API ContentTransform para definir como o conteúdo é animado durante a navegação. O NavDisplay anima automaticamente as transições entre cenas quando uma chave derivada da classe da cena atual e da propriedade key muda. Quando essa chave muda, NavDisplay usa o ContentTransform para o tipo de transição (para frente, para trás ou volta preditiva para trás) da cena apropriada na transição. Se ContentTransform não estiver definido, NavDisplay vai usar a transição padrão correspondente.

Substituir as transições padrão

É possível substituir os comportamentos de animação padrão fornecendo parâmetros de transição para NavDisplay.

  • transitionSpec: esse parâmetro define o ContentTransform a ser aplicado quando o conteúdo é adicionado ao backstack (ou seja, ao navegar para frente).
  • popTransitionSpec: esse parâmetro define o ContentTransform a ser aplicado quando o conteúdo é removido da backstack (ou seja, ao navegar para trás).
  • predictivePopTransitionSpec: esse parâmetro define a ContentTransform a ser aplicada quando o conteúdo é exibido usando um gesto de retorno preditivo.

Substituir transições no nível Scene

É possível usar metadados para definir animações personalizadas para cenas individuais usando as seguintes chaves de metadados definidas por NavDisplay:

Quando fornecidas, essas transições no nível da cena são usadas em vez dos padrões correspondentes definidos no NavDisplay.

O snippet a seguir demonstra transições globais de NavDisplay e uma substituição no nível individual de NavEntry:

@Serializable
data object ScreenA : NavKey

@Serializable
data object ScreenB : NavKey

@Serializable
data object ScreenC : NavKey

class AnimatedNavDisplayActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {

            Scaffold { paddingValues ->

                val backStack = rememberNavBackStack(ScreenA)

                NavDisplay(
                    backStack = backStack,
                    onBack = { backStack.removeLastOrNull() },
                    entryProvider = entryProvider {
                        entry<ScreenA> {
                            ContentOrange("This is Screen A") {
                                Button(onClick = { backStack.add(ScreenB) }) {
                                    Text("Go to Screen B")
                                }
                            }
                        }
                        entry<ScreenB> {
                            ContentMauve("This is Screen B") {
                                Button(onClick = { backStack.add(ScreenC) }) {
                                    Text("Go to Screen C")
                                }
                            }
                        }
                        entry<ScreenC>(
                            metadata = metadata {
                                put(NavDisplay.TransitionKey) {
                                    // Slide new content up, keeping the old content in place underneath
                                    slideInVertically(
                                        initialOffsetY = { it },
                                        animationSpec = tween(1000)
                                    ) togetherWith ExitTransition.KeepUntilTransitionsFinished
                                }
                                put(NavDisplay.PopTransitionKey) {
                                    // Slide old content down, revealing the new content in place underneath
                                    EnterTransition.None togetherWith
                                            slideOutVertically(
                                                targetOffsetY = { it },
                                                animationSpec = tween(1000)
                                            )
                                }
                                put(NavDisplay.PredictivePopTransitionKey) {
                                    // Slide old content down, revealing the new content in place underneath
                                    EnterTransition.None togetherWith
                                            slideOutVertically(
                                                targetOffsetY = { it },
                                                animationSpec = tween(1000)
                                            )
                                }
                            }
                        ) {
                            ContentGreen("This is Screen C")
                        }
                    },
                    transitionSpec = {
                        // Slide in from right when navigating forward
                        slideInHorizontally(initialOffsetX = { it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { -it })
                    },
                    popTransitionSpec = {
                        // Slide in from left when navigating back
                        slideInHorizontally(initialOffsetX = { -it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { it })
                    },
                    predictivePopTransitionSpec = {
                        // Slide in from left when navigating back
                        slideInHorizontally(initialOffsetX = { -it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { it })
                    },
                    modifier = Modifier.padding(paddingValues)
                )
            }
        }
    }
}

Figura 1. App com animações personalizadas.

Fazer a transição de entradas de navegação entre cenas

Em apps que criam layouts personalizados usando cenas, é possível que um NavEntry seja incluído na propriedade entries das duas cenas durante uma transição. Internamente, NavDisplay verifica se cada entrada é mostrada em no máximo uma cena por vez, o que pode resultar em transições instáveis quando a cena que renderiza um NavEntry muda. Para animar as entradas entre cenas, ajuste o NavDisplay em um SharedTransitionLayout e forneça o SharedTransitionScope ao NavDisplay, conforme mostrado no exemplo a seguir:

SharedTransitionLayout {
    NavDisplay(
        // ...
        sharedTransitionScope = this
    )
}