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

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

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

Для обеспечения удобства использования крайне важно, чтобы состояние навигации вашего приложения сохранялось на протяжении различных этапов жизненного цикла, включая изменения конфигурации и завершение процессов. В 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)
}

Помните о стеке возврата с подтипами NavKey

Компонуемая функция rememberNavBackStack возвращает объект NavBackStack<NavKey> . Если ваше приложение определяет собственный подтип NavKey , от которого наследуются все его клавиши, вы можете сохранить эту типизацию, реализовав пользовательскую функцию remember следующим образом:

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

Дополнительные примеры, в том числе о том, как обрабатывать открытый полиморфизм, см. NavBackStackSamples .

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

Другой подход к управлению стеком обработки событий — хранение его в 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. Добавьте в список entryDecorators ) функцию rememberSaveableStateHolderNavEntryDecorator() , используемую по умолчанию, при создании NavDisplay .
  3. Добавьте rememberViewModelStoreNavEntryDecorator() в список 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 { },
)