Comprendre et implémenter les principes de base

La navigation décrit la façon dont les utilisateurs se déplacent dans votre application. Ils interagissent avec les éléments de l'interface utilisateur, généralement en appuyant ou en cliquant dessus, et l'application répond en affichant un nouveau contenu. Si l'utilisateur souhaite revenir au contenu précédent, il utilise le geste Retour ou appuie sur le bouton Retour.

Modéliser l'état de navigation

Une façon pratique de modéliser ce comportement consiste à utiliser une pile de contenu. Lorsque l'utilisateur navigue vers l'avant vers un nouveau contenu, celui-ci est placé en haut de la pile. Lorsqu'il revient en arrière, le contenu est retiré de la pile et le contenu précédent s'affiche. En termes de navigation, cette pile est généralement appelée pile "Retour" , car elle représente le contenu auquel l'utilisateur peut revenir.

Bouton d'action du clavier virtuel (icône en forme de coche) entouré en rouge.
Figure 1. Diagramme montrant comment la pile "Retour" change en fonction des événements de navigation de l'utilisateur.

Créer une pile "Retour"

Dans Navigation 3, la pile "Retour" ne contient pas de contenu. Elle contient plutôt des références au contenu, appelées clés. Les clés peuvent être de n'importe quel type, mais il s'agit généralement de classes de données sérialisables simples. L'utilisation de références plutôt que de contenu présente les avantages suivants :

  • Il est simple de naviguer en plaçant des clés dans la pile "Retour".
  • Tant que les clés sont sérialisables, la pile "Retour" peut être enregistrée dans un stockage persistant, ce qui lui permet de survivre aux modifications de configuration et à la fin du processus. Ceci est important, car les utilisateurs s'attendent à pouvoir quitter votre application, y revenir plus tard et reprendre là où ils s'étaient arrêtés, avec le même contenu affiché. Pour en savoir plus, consultez Enregistrer votre pile "Retour".

Un concept clé de l'API Navigation 3 est que vous possédez la pile "Retour". La bibliothèque :

  • s'attend à ce que votre pile "Retour" soit un List<T> sauvegardé par un état d'instantané, où T est le type de vos keys de pile "Retour". Vous pouvez utiliser Any ou fournir vos propres clés plus fortement typées. Lorsque vous voyez les termes "push" ou "pop", l'implémentation sous-jacente consiste à ajouter ou à supprimer des éléments à la fin d'une liste.
  • observe votre pile "Retour" et reflète son état dans l'UI à l'aide d'un NavDisplay.

L'exemple suivant montre comment créer des clés et une pile "Retour", et comment modifier la pile "Retour" en réponse aux événements de navigation de l'utilisateur :

// Define keys that will identify content
data object ProductList
data class ProductDetail(val id: String)

@Composable
fun MyApp() {

    // Create a back stack, specifying the key the app should start with
    val backStack = remember { mutableStateListOf<Any>(ProductList) }

    // Supply your back stack to a NavDisplay so it can reflect changes in the UI
    // ...more on this below...

    // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state
    backStack.add(ProductDetail(id = "ABC"))

    // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state
    backStack.removeLastOrNull()
}

Résoudre les clés en contenu

Le contenu est modélisé dans Navigation 3 à l'aide de NavEntry, qui est une classe contenant une fonction composable. Il représente une destination , un élément unique de contenu vers lequel l'utilisateur peut naviguer vers l'avant et vers l'arrière.

Un NavEntry peut également contenir des métadonnées, c'est-à-dire des informations sur le contenu. Ces métadonnées peuvent être lues par des objets conteneurs, tels que NavDisplay, pour les aider à décider comment afficher le contenu de NavEntry. Par exemple, les métadonnées peuvent être utilisées pour remplacer les animations par défaut d'un NavEntry spécifique. Les metadata de NavEntry sont une carte de clés String vers des valeurs Any, ce qui permet un stockage de données polyvalent.

Pour convertir une key en NavEntry, créez un fournisseur d'entrées. Il s'agit d'une fonction qui accepte une key et renvoie un NavEntry pour cette key. Elle est généralement définie comme paramètre lambda lors de la création d'un NavDisplay.

Il existe deux façons de créer un fournisseur d'entrées : en créant directement une fonction lambda ou en utilisant le entryProvider DSL.

Créer directement une fonction de fournisseur d'entrées

Vous créez généralement une fonction de fournisseur d'entrées à l'aide d'une instruction when, avec une branche pour chacune de vos clés.

entryProvider = { key ->
    when (key) {
        is ProductList -> NavEntry(key) { Text("Product List") }
        is ProductDetail -> NavEntry(
            key,
            metadata = mapOf("extraDataKey" to "extraDataValue")
        ) { Text("Product ${key.id} ") }

        else -> {
            NavEntry(Unit) { Text(text = "Invalid Key: $it") }
        }
    }
}

Utiliser le DSL entryProvider

Le DSL entryProvider peut simplifier votre fonction lambda en évitant d'avoir à tester chaque type de clé et à construire un NavEntry pour chacun d'eux. Pour ce faire, utilisez la fonction de compilation entryProvider. Elle inclut également un comportement de secours par défaut (générer une erreur) si la clé n'est pas trouvée.

entryProvider = entryProvider {
    entry<ProductList> { Text("Product List") }
    entry<ProductDetail>(
        metadata = mapOf("extraDataKey" to "extraDataValue")
    ) { key -> Text("Product ${key.id} ") }
}

Notez les points suivants dans l'extrait :

  • entry permet de définir un NavEntry avec le type et le contenu composable donnés.
  • entry accepte un paramètre metadata pour définir NavEntry.metadata.

Afficher la pile "Retour"

La pile "Retour" représente l'état de navigation de votre application. Chaque fois que la pile "Retour" change, l'UI de l'application doit refléter le nouvel état de la pile "Retour". Dans Navigation 3, un NavDisplay observe votre pile "Retour" et met à jour son UI en conséquence. Construisez-le avec les paramètres suivants :

  • Votre pile "Retour" doit être de type SnapshotStateList<T>, où T est le type de vos clés de pile "Retour". Il s'agit d'une List observable qui déclenche la recomposition de NavDisplay lorsqu'elle change.
  • Un entryProvider pour convertir les clés de votre pile "Retour" en objets NavEntry.
  • Vous pouvez également fournir un lambda au paramètre onBack. Il est appelé lorsque l'utilisateur déclenche un événement de retour.

L'exemple suivant montre comment créer un NavDisplay.

data object Home
data class Product(val id: String)

@Composable
fun NavExample() {

    val backStack = remember { mutableStateListOf<Any>(Home) }

    NavDisplay(
        backStack = backStack,
        onBack = { backStack.removeLastOrNull() },
        entryProvider = { key ->
            when (key) {
                is Home -> NavEntry(key) {
                    ContentGreen("Welcome to Nav3") {
                        Button(onClick = {
                            backStack.add(Product("123"))
                        }) {
                            Text("Click to navigate")
                        }
                    }
                }

                is Product -> NavEntry(key) {
                    ContentBlue("Product ${key.id} ")
                }

                else -> NavEntry(Unit) { Text("Unknown route") }
            }
        }
    )
}

Par défaut, le NavDisplay affiche le NavEntry le plus haut de la pile "Retour" dans une mise en page à un seul volet. L'enregistrement suivant montre l'exécution de cette application :

Comportement par défaut de `NavDisplay` avec deux destinations.
Figure 2. NavDisplay comportement par défaut avec deux destinations.

Cycle de vie de la destination

NavDisplay utilise des custom LifecycleOwners personnalisés pour limiter l'état du cycle de vie d'un NavEntry en fonction des contraintes au niveau de la Scene et de l'entrée contraintes.

Pour en savoir plus sur les cycles de vie dans Compose, consultez Cycle de vie dans Jetpack Compose.

Contraintes de cycle de vie au niveau de la scène

NavDisplay gère le cycle de vie des Scene actifs. Les limites au niveau de la scène sont déterminées comme suit :

Pour les scènes sans superposition :

  • RESUMED: autorisé uniquement lorsque la transition de scène est terminée et qu'aucune scène de superposition active ne s'affiche au-dessus.
  • STARTED: limité à STARTED pendant les transitions de scène, par exemple lors d'une navigation vers l'avant ou vers l'arrière, ou lorsqu'il est couvert par une superposition.

Pour les scènes de superposition, telles que les boîtes de dialogue ou les feuilles inférieures :

  • RESUMED: autorisé uniquement pour la scène de superposition active la plus haute.
  • STARTED: limité à STARTED pour toutes les scènes de superposition sous-jacentes couvertes par une superposition plus récente.

État du cycle de vie au niveau de l'entrée

La bibliothèque gère l'état maximal du cycle de vie de chaque NavEntry en fonction de sa présence dans la pile "Retour" :

  • RESUMED: si l'entrée est présente dans la pile "Retour" actuelle, son cycle de vie peut atteindre RESUMED (sous réserve de la limite au niveau de la scène).
  • CREATED : si l'entrée ne se trouve plus dans la pile "Retour", par exemple lorsqu'elle a été retirée, mais qu'elle est toujours affichée à l'écran pendant l' animation de sortie, la bibliothèque limite strictement son cycle de vie à CREATED. Cette limite garantit que les entrées en arrière-plan ou en sortie cessent d'exécuter des tâches actives telles que la collecte de flux ou le lancement de coroutines liées aux états RESUMED ou STARTED pendant qu'elles terminent leurs transitions de sortie.

Combinaison

Par exemple, l'état final du cycle de vie d'un NavEntry est résolu comme suit :

Scénario Limite au niveau de la scène Limite au niveau de l'entrée Limite effective
Entrée active, écran fixe (aucune transition ni superposition) RESUMED RESUMED RESUMED
Entrée active, pendant la transition (navigation vers ou depuis) STARTED RESUMED STARTED
Entrée active, couverte par une superposition (par exemple, une boîte de dialogue est ouverte) STARTED RESUMED STARTED
Entrée retirée, animation de sortie STARTED ou RESUMED CREATED CREATED

Synthèse

Le schéma suivant montre comment les données circulent entre les différents objets de Navigation 3 :

Visualisation du flux de données entre les différents objets de Navigation 3.
Figure 3. Schéma montrant comment les données circulent entre les différents objets de Navigation 3.
  1. Les événements de navigation initient des modifications. Des clés sont ajoutées ou supprimées de la pile "Retour" en réponse aux interactions de l'utilisateur.

  2. La modification de l'état de la pile "Retour" déclenche la récupération du contenu. Le NavDisplay (un composable qui affiche une pile "Retour") observe la pile "Retour". Dans sa configuration par défaut, il affiche l'entrée de pile "Retour" la plus haute dans une mise en page à un seul volet. Lorsque la clé supérieure de la pile "Retour" change, le NavDisplay utilise cette clé pour demander le contenu correspondant au fournisseur d'entrées.

  3. Le fournisseur d'entrées fournit le contenu. Le fournisseur d'entrées est une fonction qui résout une clé en NavEntry. Lorsqu'il reçoit une clé du NavDisplay, le fournisseur d'entrées fournit le NavEntry associé, qui contient à la fois la clé et le contenu.

  4. Le contenu s'affiche. Le NavDisplay reçoit le NavEntry et affiche le contenu.