Сохранение и управление состоянием навигации

В следующих разделах описываются стратегии сохранения стека переходов и состояния, связанного с записями в стеке переходов.

Сохраните свой стек

Обеспечение сохранения состояния навигации вашего приложения в различных событиях жизненного цикла, включая изменения конфигурации и завершение процесса, имеет решающее значение для хорошего пользовательского опыта. В Navigation 3 вы являетесь владельцем своего стека переходов назад, поэтому нет строгих указаний о том, как его создавать или сохранять. Однако Navigation 3 предлагает удобный метод, который предоставляет вам сохраняемый стек переходов назад: rememberNavBackStack .

Используйте rememberNavBackStack

Компонуемая функция rememberNavBackStack предназначена для создания обратного стека, который сохраняется при изменении конфигурации и завершении процесса.

Для корректной работы rememberNavBackStack каждая клавиша в вашем стеке должна соответствовать определенным требованиям:

  • Реализовать интерфейс NavKey : каждый ключ в стеке возврата должен реализовывать интерфейс NavKey . Это действует как интерфейс маркера, который сигнализирует библиотеке о том, что ключ может быть сохранен.
  • Наличие аннотации @Serializable : Помимо реализации NavKey , ваши ключевые классы и объекты должны быть отмечены аннотацией @Serializable .

В следующем фрагменте показана правильная реализация rememberNavBackStack :

@Serializable
data object Home : NavKey

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

Альтернатива: хранение в ViewModel

Другой подход к управлению вашим back stack заключается в его сохранении в ViewModel . Для сохранения после смерти процесса при использовании ViewModel или любого другого пользовательского хранилища вам необходимо:

  • Убедитесь, что ваши ключи сериализуемы : как и в случае с rememberNavBackStack , ваши навигационные клавиши должны быть сериализуемыми.
  • Выполняйте сериализацию и десериализацию вручную : вы несете ответственность за ручное сохранение сериализованного представления каждого ключа в постоянном хранилище (например, SharedPreferences , базе данных или файле) и его десериализацию из него, когда ваше приложение переходит в фоновый режим или восстанавливается.

Область действия ViewModel в NavEntry

ViewModels используются для сохранения состояния, связанного с пользовательским интерфейсом, при изменении конфигурации, например, при поворотах экрана. По умолчанию ViewModels ограничены ближайшим ViewModelStoreOwner , который обычно является вашим Activity или Fragment .

Однако вам может потребоваться ограничить ViewModel определенным NavEntry (т. е. определенным экраном или пунктом назначения) в стеке переходов назад, а не всем Activity . Это гарантирует, что состояние ViewModel сохраняется только пока этот конкретный NavEntry является частью стека переходов назад, и очищается при извлечении NavEntry .

Библиотека дополнений androidx.lifecycle:lifecycle-viewmodel-navigation3 предоставляет NavEntryDecorator , который облегчает это. Этот декоратор предоставляет ViewModelStoreOwner для каждого NavEntry . Когда вы создаете ViewModel внутри содержимого NavEntry (например, используя viewModel() в Compose), он автоматически ограничивается этим конкретным ключом NavEntry в стеке переходов. Это означает, что ViewModel создается при добавлении NavEntry в стек переходов и очищается при его удалении.

Чтобы использовать NavEntryDecorator для ограничения области действия ViewModel до NavEntry , выполните следующие действия:

  1. Добавьте зависимость androidx.lifecycle:lifecycle-viewmodel-navigation3 в файл app/build.gradle.kts .
  2. Добавьте rememberSavedStateNavEntryDecorator() в список entryDecorators при построении NavDisplay .
  3. Добавьте другие декораторы в ваш 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 { },
)