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 il back stack

Garantire che lo stato di navigazione dell'app persista tra i vari eventi del ciclo di vita, incluse le modifiche alla configurazione e l'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 un back stack salvabile: rememberNavBackStack.

Utilizzare rememberNavBackStack

La funzione componibile rememberNavBackStack è progettata per creare uno stack indietro che persiste tra le modifiche alla configurazione e l'interruzione del processo.

Affinché rememberNavBackStack funzioni correttamente, ogni chiave del back stack deve rispettare requisiti specifici:

  • Implementare l'interfaccia NavKey: ogni chiave del back stack 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 un back stack 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 del back stack è archiviarlo in un ViewModel. Per la persistenza durante l'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.

Applicare l'ambito dei ViewModel agli NavEntry

ViewModels vengono utilizzati per conservare lo stato correlato all'UI tra le modifiche alla configurazione, ad esempio le rotazioni dello schermo. Per impostazione predefinita, ViewModels hanno come ambito il ViewModelStoreOwner più vicino, in genere Activity o Fragment.

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

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 applicato automaticamente alla chiave NavEntry specifica nel back stack. Ciò significa che ViewModel viene creato quando NavEntry viene aggiunto al back stack e cancellato quando viene rimosso.

Per utilizzare NavEntryDecorator per applicare l'ambito dei ViewModel agli NavEntry, segui questi passaggi:

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