В следующих разделах описаны стратегии сохранения стека возврата и хранения состояния, связанного с записями в стеке возврата.
Как сохранить историю переходов назад
Чтобы пользователям было удобно работать с приложением, важно, чтобы состояние навигации сохранялось при различных событиях жизненного цикла, в том числе при изменении конфигурации и завершении процесса. В Navigation 3 вы сами управляете стеком возврата, поэтому строгих правил по его созданию и сохранению нет. Однако в Navigation 3 есть удобный метод, который позволяет сохранить стек возврата: rememberNavBackStack.
Назначить rememberNavBackStack
rememberNavBackStack composable-функция предназначена для создания обратного стека, который сохраняется при изменении конфигурации и завершении процесса.
Чтобы кнопка rememberNavBackStack работала правильно, каждый ключ в стеке возврата должен соответствовать определенным требованиям:
- Реализуйте интерфейс
NavKey. Каждый ключ в стеке возврата должен реализовывать интерфейсNavKey. Это интерфейс-маркер, который сообщает библиотеке, что ключ можно сохранить. - Используйте аннотацию
@Serializable. Помимо реализацииNavKey, ключевые классы и объекты должны быть отмечены аннотацией@Serializable.
Ниже приведен фрагмент кода с правильной реализацией метода rememberNavBackStack:
@Serializable data object Home : NavKey @Composable fun NavBackStack() { val backStack = rememberNavBackStack(Home) }
Запомнить историю переходов с подтипами NavKey
Composable-функция 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, выполните следующие действия:
- Добавьте зависимость
androidx.lifecycle:lifecycle-viewmodel-navigation3в файлapp/build.gradle.kts. - Добавьте значение по умолчанию
rememberSaveableStateHolderNavEntryDecorator()в списокentryDecoratorsпри созданииNavDisplay. - Добавьте
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 { }, )