Cómo animar entre destinos

NavDisplay proporciona capacidades de animación integradas para crear transiciones visuales fluidas a medida que los usuarios navegan por tu app. Puedes personalizar estas animaciones de forma global para NavDisplay o a nivel de Scene con metadatos.

Comprende las capacidades de animación integradas

NavDisplay usa la API de ContentTransform para definir cómo se anima el contenido durante la navegación. NavDisplay anima automáticamente las transiciones entre escenas cuando cambia una clave derivada de la clase de la escena actual y su propiedad key. Cuando cambia esta clave, NavDisplay usa el ContentTransform para el tipo de transición (hacia adelante, hacia atrás o atrás predictivo) de la escena adecuada en la transición. Si no se define ese ContentTransform, NavDisplay recurre a su transición predeterminada correspondiente.

Cómo anular las transiciones predeterminadas

Puedes anular los comportamientos de animación predeterminados si proporcionas parámetros de transición a NavDisplay.

  • transitionSpec: Este parámetro define el ContentTransform que se aplicará cuando se agregue contenido a la pila de actividades (es decir, cuando se navegue hacia adelante).
  • popTransitionSpec: Este parámetro define el ContentTransform que se aplicará cuando se quite contenido de la pila de actividades (es decir, cuando se navegue hacia atrás).
  • predictivePopTransitionSpec: Este parámetro define el ContentTransform que se aplicará cuando se muestre contenido con un gesto de atrás predictivo.

Anula las transiciones a nivel de Scene

Puedes usar metadatos para definir animaciones personalizadas para escenas individuales con las siguientes claves de metadatos definidas por NavDisplay:

Cuando se proporcionan, estas transiciones a nivel de la escena se usan en lugar de los valores predeterminados correspondientes establecidos en NavDisplay.

En el siguiente fragmento, se muestran las transiciones globales de NavDisplay y una anulación a nivel de NavEntry individual:

@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 con animaciones personalizadas.

Transición de entradas de navegación entre escenas

En las apps que crean diseños personalizados con escenas, es posible que se incluya un NavEntry en la propiedad entries de ambas escenas durante una transición. Internamente, NavDisplay verifica que cada entrada se muestre en, como máximo, una escena en cualquier momento, lo que puede generar transiciones bruscas cuando cambia la escena que renderiza un NavEntry. Para animar las entradas entre escenas de forma fluida, puedes incluir tu NavDisplay en un SharedTransitionLayout y proporcionar el SharedTransitionScope al NavDisplay como se muestra en el siguiente ejemplo:

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