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
NavKey
interface: Every key in the back stack must implement theNavKey
interface. This acts as a marker interface that signals to the library that the key can be saved. - Have the
@Serializable
annotation: In addition to implementingNavKey
, your key classes and objects must be marked with the@Serializable
annotation.
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 ViewModel
s to NavEntry
s
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 ViewModel
s to NavEntry
s, follow these
steps:
- Add the
androidx.lifecycle:lifecycle-viewmodel-navigation3
dependency to yourapp/build.gradle.kts
file. - Add
rememberSavedStateNavEntryDecorator()
to the list ofentryDecorators
when 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 { }, )