Il NavController contiene un "back stack" che include le destinazioni visitate dall'utente. Man mano che l'utente naviga tra le schermate dell'app, NavController aggiunge e rimuove le destinazioni dallo stack indietro.
Essendo uno stack, il back stack è una struttura di dati "last in, first out". Pertanto, NavController inserisce e rimuove gli elementi dalla parte superiore dello stack.
Comportamento di base
Ecco i fatti principali da considerare in merito al comportamento dello stack indietro:
- Prima destinazione:quando l'utente apre l'app,
NavControllerinserisce la prima destinazione nella parte superiore del back stack. - Inserimento nello stack: ogni chiamata
NavController.navigate()inserisce la destinazione specificata nella parte superiore dello stack. - Rimozione della destinazione principale: toccando Su o Indietro vengono chiamati rispettivamente i metodi
NavController.navigateUp()eNavController.popBackStack(). Questi metodi rimuovono la destinazione principale dallo stack. Per ulteriori informazioni sulla differenza tra Su e Indietro, consulta la pagina Principi di navigazione.
Tornare indietro
Il metodo NavController.popBackStack() tenta di rimuovere la destinazione corrente dallo stack indietro e di passare alla destinazione precedente. In pratica, l'utente torna indietro di un passaggio nella cronologia di navigazione. Restituisce un valore booleano che indica se è stato possibile tornare alla destinazione.
Tornare a una destinazione specifica
Puoi anche utilizzare popBackStack() per passare a una destinazione specifica. Per farlo, utilizza uno dei suoi overload. Esistono diversi overload che consentono di passare un identificatore, ad esempio un id intero o una route stringa. Questi overload portano l'utente alla destinazione associata all'identificatore specificato. In particolare, rimuovono tutto ciò che si trova nello stack sopra la destinazione.
Questi overload accettano anche un valore booleano inclusive. Determina se NavController deve rimuovere anche la destinazione specificata dal back stack dopo averla raggiunta.
Ecco un breve snippet di esempio:
navController.popBackStack(R.id.destinationId, true)
In questo caso, NavController torna alla destinazione con l'ID intero destinationId. Poiché il valore dell'argomento inclusive è true, NavController rimuove anche la destinazione specificata dal back stack.
Gestire un'operazione di ritorno non riuscita
Quando popBackStack() restituisce false, una chiamata successiva a NavController.getCurrentDestination() restituisce null. Ciò significa che l'app ha rimosso l'ultima destinazione dal back stack. In questo caso, l'utente vede solo una schermata vuota.
Questo può verificarsi nei seguenti casi:
popBackStack()non ha rimosso nulla dallo stack.popBackStack()ha rimosso una destinazione dal back stack e ora lo stack è vuoto.
Per risolvere il problema, devi passare a una nuova destinazione o chiamare finish() sull'attività per terminarla. Lo snippet seguente mostra come fare:
kotlin
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish()
}
java
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish();
}
Passare a una destinazione
Per rimuovere le destinazioni dal back stack quando passi da una destinazione all'altra, aggiungi un argomento popUpTo() alla chiamata di funzione navigate() associata. popUpTo() indica alla libreria Navigation di rimuovere alcune destinazioni dal back stack nell'ambito della chiamata a navigate(). Il valore del parametro è l'identificatore di una destinazione nel back stack. L'identificatore può essere un id intero o una route stringa.
Puoi includere un argomento per il parametro inclusive con un valore di true per indicare che la destinazione specificata in popUpTo() deve essere rimossa anche dallo stack indietro.
Per implementare questa operazione a livello di programmazione, passa popUpTo() a navigate() come parte di
NavOptions con inclusive impostato su true. Questa operazione funziona sia in Compose che in Views.
Salvare lo stato durante il passaggio
Quando utilizzi popUpTo per passare a una destinazione, puoi facoltativamente salvare il back stack e gli stati di tutte le destinazioni rimosse dal back stack. Puoi quindi ripristinare lo stack indietro e le destinazioni quando passi a quella destinazione in un secondo momento. In questo modo puoi conservare lo stato di una determinata destinazione e avere
più stack indietro.
Per farlo a livello di programmazione, specifica saveState = true quando aggiungi popUpTo alle opzioni di navigazione.
Puoi anche specificare restoreState = true nelle opzioni di navigazione per ripristinare automaticamente il back stack e lo stato associato alla destinazione.
Ad esempio:
navController.navigate(
route = route,
navOptions = navOptions {
popUpTo<A>{ saveState = true }
restoreState = true
}
)
Per abilitare il salvataggio e il ripristino dello stato in XML, definisci popUpToSaveState come true
e restoreState come true rispettivamente nell'action associata.
Esempio XML
Ecco un esempio di popUpTo in XML, utilizzando un'azione:
<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"/>
Esempio di Compose
Di seguito è riportato un esempio completo della stessa operazione in Compose:
@Composable
fun MyAppNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
startDestination: Any = A
) {
NavHost(
modifier = modifier,
navController = navController,
startDestination = startDestination
) {
composable<A> {
DestinationA(
onNavigateToB = {
// Pop everything up to, and including, the A destination off
// the back stack, saving the back stack and the state of its
// destinations.
// Then restore any previous back stack state associated with
// the B destination.
// Finally navigate to the B destination.
navController.navigate(route = B) {
popUpTo<A> {
inclusive = true
saveState = true
}
restoreState = true
}
},
)
}
composable<B> { DestinationB(/* ... */) }
}
}
@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
Button(onClick = onNavigateToB) {
Text("Go to A")
}
}
In modo più granulare, puoi modificare il modo in cui chiami NavController.navigate() nei seguenti modi:
// 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
}
Per informazioni generali sul passaggio delle opzioni a NavController.navigate(), consulta
la guida Navigare con le opzioni.
Passare utilizzando le azioni
Quando navighi utilizzando un'azione, puoi facoltativamente rimuovere altre destinazioni dal back stack. Ad esempio, se la tua app ha un flusso di accesso iniziale, una volta che un utente ha eseguito l'accesso, devi rimuovere tutte le destinazioni correlate all'accesso dallo stack indietro in modo che il pulsante Indietro non riporti gli utenti al flusso di accesso.
Letture aggiuntive
Per ulteriori informazioni, consulta le seguenti pagine:
- Navigazione circolare: scopri come evitare uno stack indietro troppo pieno nei casi in cui i flussi di navigazione sono circolari.
- Destinazioni di dialogo: scopri in che modo le destinazioni di dialogo introducono considerazioni uniche sulla gestione del back stack.