Animare il passaggio da una destinazione all'altra

NavDisplay offre funzionalità di animazione integrate per creare transizioni visive fluide mentre gli utenti navigano nell'app. Puoi personalizzare queste animazioni a livello globale per NavDisplay o a livello di Scene utilizzando i metadati.

Comprendere le funzionalità di animazione integrate

NavDisplay utilizza l'API ContentTransform per definire l'animazione dei contenuti durante la navigazione. NavDisplay anima automaticamente le transizioni tra le scene quando cambia una chiave derivata dalla classe della scena corrente e dalla relativa proprietà key. Quando questa chiave cambia, NavDisplay utilizza ContentTransform per il tipo di transizione (avanti, indietro o indietro predittivo) dalla scena appropriata della transizione. Se ContentTransform non è definito, NavDisplay utilizza la transizione predefinita corrispondente.

Sostituisci le transizioni predefinite

Puoi ignorare i comportamenti di animazione predefiniti fornendo parametri di transizione a NavDisplay.

  • transitionSpec: questo parametro definisce la transizione ContentTransform da applicare quando i contenuti vengono aggiunti allo stack precedente (ovvero quando si naviga in avanti).
  • popTransitionSpec: questo parametro definisce la ContentTransform da applicare quando i contenuti vengono rimossi dallo stack precedente (ovvero quando si torna indietro).
  • predictivePopTransitionSpec: questo parametro definisce l'ContentTransform da applicare quando i contenuti vengono visualizzati utilizzando un gesto di indietro predittivo.

Ignorare le transizioni a livello di Scene

Puoi utilizzare i metadati per definire animazioni personalizzate per le singole scene utilizzando le seguenti chiavi dei metadati definite da NavDisplay:

Se fornite, queste transizioni a livello di scena vengono utilizzate al posto dei valori predefiniti corrispondenti impostati in NavDisplay.

Lo snippet seguente mostra sia le transizioni globali di NavDisplay sia un override a livello di singolo 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 con animazioni personalizzate.

Transizione delle voci di navigazione tra le scene

Nelle app che creano layout personalizzati utilizzando le scene, è possibile che un NavEntry venga incluso nella proprietà entries di entrambe le scene durante una transizione. A livello interno, NavDisplay verifica che ogni voce venga visualizzata in al massimo una scena alla volta, il che può comportare transizioni instabili quando la scena che esegue il rendering di un NavEntry cambia. Per animare in modo fluido le voci tra le scene, puoi racchiudere NavDisplay in un SharedTransitionLayout e fornire SharedTransitionScope a NavDisplay come mostrato nell'esempio seguente:

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