Animer des transitions entre les destinations

NavDisplay fournit des fonctionnalités d'animation intégrées pour créer des transitions visuelles fluides lorsque les utilisateurs naviguent dans votre application. Vous pouvez personnaliser ces animations de manière globale pour NavDisplay ou au niveau de Scene à l'aide de métadonnées.

Comprendre les fonctionnalités d'animation intégrées

NavDisplay utilise l'API ContentTransform pour définir la façon dont le contenu s'anime lors de la navigation. NavDisplay anime automatiquement les transitions entre les scènes lorsqu'une clé dérivée de la classe de la scène actuelle et de sa propriété key change. Lorsque cette clé change, NavDisplay utilise ContentTransform pour le type de transition (avant, arrière ou retour prédictif) à partir de la scène appropriée de la transition. Si ContentTransform n'est pas défini, NavDisplay utilise sa transition par défaut correspondante.

Remplacer les transitions par défaut

Vous pouvez remplacer les comportements d'animation par défaut en fournissant des paramètres de transition à NavDisplay.

  • transitionSpec : ce paramètre définit le ContentTransform à appliquer lorsque du contenu est ajouté à la pile "Retour" (c'est-à-dire lors de la navigation vers l'avant).
  • popTransitionSpec : ce paramètre définit le ContentTransform à appliquer lorsque le contenu est supprimé de la pile "Retour" (c'est-à-dire lors de la navigation vers l'arrière).
  • predictivePopTransitionSpec : ce paramètre définit le ContentTransform à appliquer lorsque le contenu est retiré à l'aide d'un geste de prévisualisation du Retour.

Remplacer les transitions au niveau Scene

Vous pouvez utiliser des métadonnées pour définir des animations personnalisées pour des scènes individuelles à l'aide des clés de métadonnées suivantes définies par NavDisplay :

Lorsqu'elles sont fournies, ces transitions au niveau de la scène sont utilisées à la place des valeurs par défaut correspondantes définies sur NavDisplay.

L'extrait suivant illustre à la fois les transitions NavDisplay globales et un remplacement au niveau de chaque 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)
                )
            }
        }
    }
}

Figure 1. Application avec des animations personnalisées.

Faire passer les entrées de navigation d'une scène à une autre

Dans les applications qui créent des mises en page personnalisées à l'aide de scènes, il est possible qu'un NavEntry soit inclus dans la propriété entries des deux scènes lors d'une transition. En interne, NavDisplay vérifie que chaque entrée est affichée dans au maximum une scène à la fois, ce qui peut entraîner des transitions saccadées lorsque la scène affichant un NavEntry change. Pour animer en douceur les entrées entre les scènes, vous pouvez encapsuler votre NavDisplay dans un SharedTransitionLayout et fournir le SharedTransitionScope au NavDisplay, comme indiqué dans l'exemple suivant :

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