Enregistrer et gérer l'état de navigation

Les sections suivantes décrivent les stratégies permettant d'enregistrer votre pile "Retour" et de stocker l'état associé aux entrées de votre pile "Retour".

Enregistrer votre pile "Retour"

Il est essentiel de s'assurer que l'état de navigation de votre application persiste lors de divers événements de cycle de vie, y compris les modifications de configuration et la fin du processus, pour offrir une bonne expérience utilisateur. Dans Navigation 3, vous êtes propriétaire de votre pile "Retour". Il n'existe donc pas de consignes strictes sur la façon de la créer ou de l'enregistrer. Toutefois, Navigation 3 propose une méthode pratique qui vous fournit une pile "Retour" enregistrable : rememberNavBackStack.

Utiliser rememberNavBackStack

La fonction composable rememberNavBackStack est conçue pour créer une pile "Retour" qui persiste lors des modifications de configuration et de la fin du processus.

Pour que rememberNavBackStack fonctionne correctement, chaque clé de votre pile "Retour" doit respecter des exigences spécifiques :

  • Implémenter l'interface NavKey : chaque clé de la pile "Retour" doit implémenter l'interface NavKey. Il s'agit d'une interface de marqueur qui signale à la bibliothèque que la clé peut être enregistrée.
  • Avoir l'annotation @Serializable : en plus d'implémenter NavKey, vos classes et objets clés doivent être marqués avec l'annotation @Serializable.

L'extrait suivant montre une implémentation correcte de rememberNavBackStack :

@Serializable
data object Home : NavKey

@Composable
fun NavBackStack() {
    val backStack = rememberNavBackStack(Home)
}

Mémoriser une pile "Retour" avec des sous-types de NavKey

La fonction composable rememberNavBackStack renvoie un NavBackStack<NavKey>. Si votre application définit son propre sous-type de NavKey à partir duquel toutes ses clés héritent, vous pouvez conserver cette saisie en implémentant une fonction de mémorisation personnalisée, comme suit :

@Serializable
sealed interface MyAppNavKey : NavKey

@Serializable
data object ScreenA: MyAppNavKey

@Serializable
data class ScreenB(val id: String): MyAppNavKey

@Composable
fun rememberMyAppNavBackStack(vararg elements: MyAppNavKey): NavBackStack<MyAppNavKey> {
    return rememberSerializable(serializer = serializer()) {
        NavBackStack(*elements)
    }
}

@Composable
fun MyApp() {
    // defaultNavBackStack is NavBackStack<NavKey>
    val defaultNavBackStack = rememberNavBackStack(ScreenA)
    // myAppNavBackStack is NavBackStack<MyAppNavKey>
    val myAppNavBackStack = rememberMyAppNavBackStack(ScreenA)
}

Pour obtenir d'autres exemples, y compris sur la gestion du polymorphisme ouvert, consultez NavBackStackSamples.

Autre solution : stocker dans un ViewModel

Une autre approche pour gérer votre pile "Retour" consiste à la stocker dans un ViewModel. Pour assurer la persistance lors de la fin du processus lorsque vous utilisez un ViewModel ou tout autre stockage personnalisé, vous devez procéder comme suit :

  • Vous assurer que vos clés sont sérialisables : comme avec rememberNavBackStack, vos clés de navigation doivent être sérialisables.
  • Gérer manuellement la sérialisation et la désérialisation : vous êtes responsable de l'enregistrement manuel de la représentation sérialisée de chaque clé dans un stockage persistant et de sa désérialisation (par exemple, SharedPreferences, une base de données ou un fichier) lorsque votre application passe en arrière-plan ou est restaurée.

Définir l'étendue des ViewModel sur les NavEntry

ViewModels sont utilisés pour conserver l'état lié à l'UI lors des modifications de configuration, telles que les rotations d'écran. Par défaut, ViewModels sont limités au ViewModelStoreOwner le plus proche, qui est généralement votre Activity ou Fragment.

Toutefois, vous pouvez limiter un ViewModel à un NavEntry spécifique (c'est-à-dire un écran ou une destination spécifique) dans la pile "Retour", plutôt qu'à l'ensemble de l'Activity. Cela garantit que l'état du ViewModel n'est conservé que lorsque ce NavEntry particulier fait partie de la pile "Retour" et qu'il est effacé lorsque le NavEntry est supprimé.

La bibliothèque complémentaire androidx.lifecycle:lifecycle-viewmodel-navigation3 fournit un NavEntryDecorator qui facilite cette opération. Ce décorateur fournit un ViewModelStoreOwner pour chaque NavEntry. Lorsque vous créez un ViewModel dans le contenu d'un NavEntry (par exemple, à l'aide de viewModel() dans Compose), il est automatiquement limité à la clé de ce NavEntry spécifique dans la pile "Retour". Cela signifie que le ViewModel est créé lorsque le NavEntry est ajouté à la pile "Retour" et effacé lorsqu'il est supprimé.

Pour utiliser NavEntryDecorator afin de limiter les ViewModel aux NavEntry, procédez comme suit :

  1. Ajoutez la dépendance androidx.lifecycle:lifecycle-viewmodel-navigation3 à votre fichier app/build.gradle.kts.
  2. Ajoutez le rememberSaveableStateHolderNavEntryDecorator() à la liste des entryDecorators lors de la construction d'un NavDisplay.
  3. Ajoutez rememberViewModelStoreNavEntryDecorator() à la liste des entryDecorators.

NavDisplay(
    entryDecorators = listOf(
        // Add the default decorators for managing scenes and saving state
        rememberSaveableStateHolderNavEntryDecorator(),
        // Then add the view model store decorator
        rememberViewModelStoreNavEntryDecorator()
    ),
    backStack = backStack,
    entryProvider = entryProvider { },
)