Navigation et pile "Retour"

NavController contient une pile "Retour" contenant les destinations que l'utilisateur a consultées. Lorsque l'utilisateur accède aux écrans de votre application, NavController ajoute et supprime des destinations vers et depuis la pile "Retour".

En tant que pile, la pile "Retour" est une structure de données de type "dernier arrivé, premier sorti". NavController déplace donc les éléments vers le haut et les fait sortir du haut de la pile.

Comportement de base

Voici les principaux points à prendre en compte concernant le comportement de la pile "Retour" :

  • Première destination : lorsque l'utilisateur ouvre l'application, NavController place la première destination en haut de la pile "Retour".
  • Transfert vers la pile : chaque appel de NavController.navigate() place la destination donnée en haut de la pile.
  • Affichage de la première destination : appuyer sur Haut ou Retour appelle respectivement les méthodes NavController.navigateUp() et NavController.popBackStack(). Elles font sortir la première destination de la pile. Consultez la page Principes de navigation pour en savoir plus sur la différence entre Haut et Retour.

Retour en arrière

La méthode NavController.popBackStack() tente de faire sortir la destination actuelle de la pile "Retour" et d'accéder à la destination précédente. Cela fait reculer l'utilisateur d'une étape dans son historique de navigation. Elle renvoie une valeur booléenne indiquant si elle a bien fait revenir l'utilisateur à la destination.

Revenir à une destination spécifique

Vous pouvez également utiliser popBackStack() pour accéder à une destination spécifique. Pour ce faire, utilisez l'une de ses surcharges. Plusieurs surcharges vous permettent de transmettre un identifiant, tel qu'un id entier ou une chaîne route. Ces surcharges redirigent l'utilisateur vers la destination associée à l'identifiant donné. Il est important que tous les éléments de la pile soient placés au-dessus de cette destination.

Ces surcharges prennent également une valeur booléenne inclusive, qui détermine si NavController doit également afficher la destination spécifiée de la pile "Retour" après y avoir accédé.

Prenons l'exemple suivant :

navController.popBackStack(R.id.destinationId, true)

Ici, NavController revient à la destination avec l'ID entier destinationId. Comme la valeur de l'argument inclusive est true, NavController affiche également la destination donnée de la pile "Retour".

Gérer un retour ayant échoué

Lorsque popBackStack() renvoie false, un appel ultérieur à NavController.getCurrentDestination() renvoie null. Cela signifie que l'application a fait sortir la dernière destination de la pile "Retour". Dans ce cas, l'utilisateur ne voit qu'un écran vide.

Cela peut se produire dans les cas suivants :

  • popBackStack() n'a rien supprimé de la pile.
  • popBackStack() a fait sortir une destination de la pile "Retour" et celle-ci est désormais vide.

Pour résoudre ce problème, vous devez ensuite accéder à une nouvelle destination ou appeler finish() sur votre activité pour l'arrêter, comme le montre l'extrait de code suivant :

kotlin

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

java

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

Accéder à une destination

Pour supprimer des destinations de la pile "Retour" lorsque vous passez d'une destination à une autre, ajoutez un argument popUpTo() à l'appel de fonction navigate() associé. popUpTo() indique à la bibliothèque Navigation de supprimer certaines destinations de la pile "Retour" dans le cadre de l'appel de navigate(). La valeur du paramètre correspond à l'identifiant d'une destination dans la pile "Retour". L'identifiant peut être un id entier ou une chaîne route.

Vous pouvez inclure un argument pour le paramètre inclusive avec la valeur true pour indiquer que la destination que vous avez spécifiée dans popUpTo() doit également sortir de la pile "Retour".

Pour implémenter ceci par programmation, transmettez popUpTo() à navigate() dans NavOptions avec inclusive défini sur true. Cela fonctionne à la fois dans Compose et dans Vues.

Enregistrer l'état lors de l'accès à une destination

Lorsque vous utilisez popUpTo pour accéder à une destination, vous pouvez éventuellement enregistrer les états de toutes les destinations sorties de la pile "Retour".

Pour activer cette option, définissez popUpToSaveState sur true dans l'action associée ou appelez NavController.navigate().

Lorsque vous accédez à une destination, vous pouvez également définir restoreSaveState sur true pour restaurer automatiquement l'état associé à la destination dans la propriété destination.

Exemple de code XML

Voici un exemple de popUpTo à l'aide d'une action en XML :

<action
  android:id="@+id/action_a_to_b"
  app:destination="@id/b"
  app:popUpTo="@+id/a"
  app:popUpToInclusive="true"
  app:restoreState=”true”
  app:popUpToSaveState="true"/>

Exemples Compose

Voici ce même exemple dans Compose :

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: String = "destination_a"
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable("destination_a") {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to the "destination_a" destination off the back stack before
                // navigating to the "destination_b" destination
                    navController.navigate("destination_b") {
                        popUpTo("destination_a") {
                            inclusive = true
                            saveState = true
                        }
                    }
                },
            )
        }
        composable("destination_b") { DestinationB(/* ... */) }
    }
}

@ Composable
fun DestinationA(onNavigateToB: () -> Unit) {
    Button(onClick = onNavigateToB) {
        Text("Go to A")
    }
}

Plus précisément, vous pouvez modifier la façon dont vous appelez NavController.navigate() de différentes manières :

// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a")
}

// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a") { inclusive = true }
}

// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
    launchSingleTop = true
}

Pour obtenir des informations générales sur la transmission d'options à NavController.navigate(), consultez le guide Naviguer avec des options.

Accéder à une destination à l'aide d'actions

Lorsque vous utilisez une action, vous pouvez éventuellement supprimer des destinations supplémentaires de la pile "Retour". Par exemple, si votre application implique un processus d'authentification initial, une fois qu'un utilisateur s'est connecté, vous devez supprimer toutes les destinations liées à l'authentification de la pile "Retour" afin que le bouton "Retour" ne redirige pas les utilisateurs vers ce processus.

Autres ressources

Pour en savoir plus, consultez les pages suivantes :

  • Navigation circulaire : découvrez comment éviter une surcharge de la pile "Retour" dans les cas où les flux de navigation sont circulaires.
  • Destinations des boîtes de dialogue : découvrez comment les destinations des boîtes de dialogue introduisent des considérations uniques sur votre façon de gérer votre pile "Retour".