Salvare e gestire lo stato di navigazione

Le sezioni seguenti descrivono le strategie per salvare lo stack indietro e archiviare lo stato associato alle voci dello stack indietro.

Salvare lo stack indietro

Garantire che lo stato di navigazione dell'app persista in vari eventi del ciclo di vita, tra cui modifiche alla configurazione e interruzione del processo, è fondamentale per una buona esperienza utente. In Navigation 3, lo stack indietro è di tua proprietà, quindi non esistono linee guida rigorose su come crearlo o salvarlo. Tuttavia, Navigation 3 offre un metodo pratico che ti fornisce uno stack indietro salvabile: rememberNavBackStack.

Utilizzare rememberNavBackStack

La funzione componibile rememberNavBackStack è progettata per creare uno stack indietro che persiste in caso di modifiche alla configurazione e interruzione del processo.

Affinché rememberNavBackStack funzioni correttamente, ogni chiave dello stack indietro deve rispettare requisiti specifici:

  • Implementare l'interfaccia NavKey: ogni chiave dello stack indietro deve implementare l'interfaccia NavKey. Questa funge da interfaccia di marcatore che segnala alla libreria che la chiave può essere salvata.
  • Avere l'annotazione @Serializable: oltre a implementare NavKey, le classi e gli oggetti chiave devono essere contrassegnati con l'annotazione @Serializable.

Il seguente snippet mostra un'implementazione corretta di rememberNavBackStack:

@Serializable
data object Home : NavKey

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

Ricordare uno stack indietro con sottotipi di NavKey

La funzione componibile rememberNavBackStack restituisce un NavBackStack<NavKey>. Se la tua app definisce un proprio sottotipo di NavKey da cui ereditano tutte le sue chiavi, puoi conservare la digitazione implementando una funzione di memorizzazione personalizzata, come segue:

@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)
}

Per altri esempi, incluso come gestire il polimorfismo aperto, consulta NavBackStackSamples.

Alternativa: archiviare in un ViewModel

Un altro approccio alla gestione dello stack indietro è archiviarlo in un ViewModel. Per la persistenza in caso di interruzione del processo quando utilizzi un ViewModel o qualsiasi altro spazio di archiviazione personalizzato, devi:

  • Assicurarti che le chiavi siano serializzabili: proprio come con rememberNavBackStack, le chiavi di navigazione devono essere serializzabili.
  • Gestire manualmente la serializzazione e la deserializzazione: quando l'app passa in background o viene ripristinata, devi salvare manualmente la rappresentazione serializzata di ogni chiave nello spazio di archiviazione permanente (ad es. SharedPreferences, un database o un file) e deserializzarla.

Definire l'ambito delle istanze ViewModel per gli oggetti NavEntry

I ViewModels vengono utilizzati per conservare lo stato correlato all'UI in caso di modifiche alla configurazione, ad esempio rotazioni dello schermo. Per impostazione predefinita, l'ambito dei ViewModels è il ViewModelStoreOwner più vicino, in genere Activity o Fragment.

Tuttavia, potresti voler definire l'ambito di un ViewModel per un NavEntry specifico (ovvero una schermata o una destinazione specifica) nello stack indietro, anziché per l'intera Activity. In questo modo, lo stato di ViewModel viene conservato solo quando il NavEntry specifico fa parte dello stack indietro e viene cancellato quando il NavEntry viene estratto.

La libreria di componenti aggiuntivi androidx.lifecycle:lifecycle-viewmodel-navigation3 fornisce un NavEntryDecorator che facilita questa operazione. Questo decoratore fornisce un ViewModelStoreOwner per ogni NavEntry. Quando crei un ViewModel all'interno dei contenuti di un NavEntry (ad es. utilizzando viewModel() in Compose), il suo ambito viene automaticamente definito per la chiave NavEntry specifica nello stack indietro. Ciò significa che il ViewModel viene creato quando il NavEntry viene aggiunto allo stack indietro e cancellato quando viene rimosso.

Per utilizzare NavEntryDecorator per definire l'ambito di ViewModel per NavEntry, segui questi passaggi:

  1. Aggiungi la dipendenza androidx.lifecycle:lifecycle-viewmodel-navigation3 al file app/build.gradle.kts.
  2. Aggiungi il rememberSaveableStateHolderNavEntryDecorator() predefinito all'elenco di entryDecorators quando crei un NavDisplay.
  3. Aggiungi rememberViewModelStoreNavEntryDecorator() all'elenco di 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 { },
)