The following sections describe strategies for saving your back stack and storing state associated with entries on your back stack.
Save your back stack
Ensuring your app's navigation state persists across various lifecycle events,
including configuration changes and process death, is crucial for a good user
experience. In Navigation 3, you own your back stack, so there aren't strict
guidelines on how you should create or save it. However, Navigation 3 does offer
a convenience method that provides you with a saveable back stack:
rememberNavBackStack.
Use rememberNavBackStack
The rememberNavBackStack composable function is designed to create a back
stack that persists across configuration changes and process death.
For rememberNavBackStack to function correctly, each key in your back stack
must adhere to specific requirements:
- Implement
NavKeyinterface: Every key in the back stack must implement theNavKeyinterface. This acts as a marker interface that signals to the library that the key can be saved. - Have the
@Serializableannotation: In addition to implementingNavKey, your key classes and objects must be marked with the@Serializableannotation.
The following snippet shows a correct implementation of rememberNavBackStack:
@Serializable data object Home : NavKey @Composable fun NavBackStack() { val backStack = rememberNavBackStack(Home) }
Alternative: Storing in a ViewModel
Another approach to managing your back stack is to store it in a ViewModel.
For persistence through process death when using a ViewModel or any other
custom storage, you need to:
- Ensure your keys are serializable: Just like with
rememberNavBackStack, your navigation keys must be serializable. - Handle serialization and deserialization manually: You're responsible for
manually saving the serialized representation of each key to, and
deserializing it from, persistent storage (e.g.,
SharedPreferences, a database, or a file) when your app is going into the background or being restored.
Scoping ViewModels to NavEntrys
ViewModels are used to retain UI-related state across configuration changes,
such as screen rotations. By default, ViewModels are scoped to the nearest
ViewModelStoreOwner, which is typically your Activity or Fragment.
However, you might want to scope a ViewModel to a specific NavEntry (i.e., a
specific screen or destination) on the back stack, rather than the entire
Activity. This ensures that the ViewModel's state is retained only while
that particular NavEntry is part of the back stack, and is cleared when the
NavEntry is popped.
The androidx.lifecycle:lifecycle-viewmodel-navigation3 add-on library provides
a NavEntryDecorator that facilitates this. This decorator provides a
ViewModelStoreOwner for each NavEntry. When you create a ViewModel inside a
NavEntry's content (e.g., using viewModel() in Compose), it is automatically
scoped to that specific NavEntry's key on the back stack. This means the
ViewModel is created when the NavEntry is added to the back stack, and
cleared when it's removed.
To use NavEntryDecorator for scoping ViewModels to NavEntrys, follow these
steps:
- Add the
androidx.lifecycle:lifecycle-viewmodel-navigation3dependency to yourapp/build.gradle.ktsfile. - Add
rememberSavedStateNavEntryDecorator()to the list ofentryDecoratorswhen constructing aNavDisplay. - Add other decorators into your
NavDisplay.
NavDisplay( entryDecorators = listOf( // Add the default decorators for managing scenes and saving state rememberSceneSetupNavEntryDecorator(), rememberSavedStateNavEntryDecorator(), // Then add the view model store decorator rememberViewModelStoreNavEntryDecorator() ), backStack = backStack, entryProvider = entryProvider { }, )