Навигация описывает способ перемещения пользователей по вашему приложению. Пользователи взаимодействуют с элементами пользовательского интерфейса, обычно касаясь или щелкая по ним, а приложение реагирует, отображая новый контент. Если пользователь хочет вернуться к предыдущему контенту, он использует жест «назад» или нажимает кнопку «назад».
Моделирование состояния навигации
Удобный способ моделирования такого поведения — использование стека контента. Когда пользователь переходит к новому контенту, он добавляется в начало стека. Когда он возвращается к предыдущему контенту, тот удаляется из стека, и отображается предыдущий контент. В контексте навигации этот стек обычно называют стеком возврата, поскольку он представляет контент, к которому пользователь может вернуться .

Создать стек возврата
В Navigation 3 стек возврата фактически не содержит контента. Вместо этого он содержит ссылки на контент , известные как ключи . Ключи могут быть любого типа, но обычно это простые сериализуемые классы данных. Использование ссылок вместо контента имеет следующие преимущества:
- Навигация проста: клавиши легко перемещаются на заднюю панель клавиатуры.
- Если ключи сериализуемы, стек возврата можно сохранить в постоянном хранилище, что позволит ему сохраняться при изменении конфигурации и завершении процесса. Это важно, поскольку пользователи ожидают, что, покинув ваше приложение, вернувшись к нему позже, они смогут продолжить с того же места, при этом отображаемый контент останется прежним. Дополнительную информацию см. в разделе «Сохранение стека возврата» .
Ключевая концепция API Navigation 3 заключается в том, что вы владеете стеком возврата. Библиотека:
- Предполагается, что ваш стек возврата будет представлять собой
List<T>, хранящийся в состоянии снимка, гдеT— типkeysвашего стека возврата. Вы можете использоватьAnyили указать свои собственные, более строго типизированные ключи. Когда вы видите термины «push» или «pop», это означает добавление или удаление элементов из конца списка. - Отслеживает состояние стека возврата и отображает его в пользовательском интерфейсе с помощью
NavDisplay.
В следующем примере показано, как создавать ключи и стек возврата, а также изменять стек возврата в ответ на события навигации пользователя:
// Define keys that will identify content data object ProductList data class ProductDetail(val id: String) @Composable fun MyApp() { // Create a back stack, specifying the key the app should start with val backStack = remember { mutableStateListOf<Any>(ProductList) } // Supply your back stack to a NavDisplay so it can reflect changes in the UI // ...more on this below... // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state backStack.add(ProductDetail(id = "ABC")) // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state backStack.removeLastOrNull() }
Разрешить ключи к содержимому
В Navigation 3 контент моделируется с помощью класса NavEntry , содержащего компонуемую функцию. Он представляет собой конечный пункт назначения — отдельный фрагмент контента, к которому пользователь может перейти и вернуться обратно .
Объект NavEntry также может содержать метаданные — информацию о содержимом. Эти метаданные могут быть прочитаны объектами-контейнерами, такими как NavDisplay , чтобы помочь им определить, как отображать содержимое NavEntry . Например, метаданные могут использоваться для переопределения анимаций по умолчанию для конкретного объекта NavEntry . metadata NavEntry представляют собой карту String ключей со значениями типа Any , обеспечивая универсальное хранение данных.
Для преобразования key в объект NavEntry создайте Entry Provider. Это функция, которая принимает key и возвращает объект NavEntry для этого key . Обычно она определяется как параметр лямбда-функции при создании NavDisplay .
Существует два способа создания поставщика входных данных: либо путем создания лямбда-функции напрямую, либо с помощью DSL entryProvider .
Создайте функцию поставщика данных напрямую.
Обычно функцию Entry Provider создают с помощью оператора when , с отдельной ветвью для каждого из ключей.
entryProvider = { key -> when (key) { is ProductList -> NavEntry(key) { Text("Product List") } is ProductDetail -> NavEntry( key, metadata = mapOf("extraDataKey" to "extraDataValue") ) { Text("Product ${key.id} ") } else -> { NavEntry(Unit) { Text(text = "Invalid Key: $it") } } } }
Используйте DSL entryProvider
DSL entryProvider может упростить вашу лямбда-функцию, избавив от необходимости проверять каждый из типов ключей и создавать NavEntry для каждого из них. Используйте для этого функцию-конструктор entryProvider . Он также включает в себя резервное поведение по умолчанию (генерация ошибки), если ключ не найден.
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
Обратите внимание на следующие фрагменты кода:
-
entryиспользуется для определения элементаNavEntryс заданным типом и составным содержимым. -
entryпринимает параметрmetadataдля установки значенияNavEntry.metadata
Отобразить стек возврата
Стек возврата (back stack) представляет собой состояние навигации вашего приложения. При каждом изменении состояния стека возврата пользовательский интерфейс приложения должен отражать новое состояние. В Navigation 3 компонент NavDisplay отслеживает состояние стека возврата и соответствующим образом обновляет свой пользовательский интерфейс. Создайте его со следующими параметрами:
- Ваш стек возврата — он должен быть типа
SnapshotStateList<T>, гдеT— тип клавиш вашего стека возврата. Это наблюдаемыйList, который запускает перекомпозициюNavDisplayпри изменении его содержимого. - Объект
entryProviderдля преобразования ключей в стеке возврата в объектыNavEntry. - При желании передайте лямбда-функцию параметру
onBack. Она вызывается, когда пользователь инициирует событие "Назад".
В следующем примере показано, как создать NavDisplay .
data object Home data class Product(val id: String) @Composable fun NavExample() { val backStack = remember { mutableStateListOf<Any>(Home) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryProvider = { key -> when (key) { is Home -> NavEntry(key) { ContentGreen("Welcome to Nav3") { Button(onClick = { backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(key) { ContentBlue("Product ${key.id} ") } else -> NavEntry(Unit) { Text("Unknown route") } } } ) }
По умолчанию NavDisplay отображает самый верхний элемент NavEntry в стеке «Назад» в однопанельном формате. Следующая запись демонстрирует работу этого приложения:

NavDisplay по умолчанию при наличии двух пунктов назначения.Жизненный цикл пункта назначения
NavDisplay использует пользовательские LifecycleOwner для ограничения состояния жизненного цикла элемента NavEntry на основе ограничений как уровня сцены , так и уровня элемента.
Для получения дополнительной информации о жизненных циклах в Compose см. раздел «Жизненный цикл в Jetpack Compose» .
Ограничения жизненного цикла на уровне сцены
NavDisplay управляет жизненным циклом активных Scene . Ограничения на уровне сцен определяются следующим образом:
Для сцен без наложения:
-
RESUMED: Разрешено только после завершения перехода между сценами и отсутствия активных наложенных сцен поверх него. -
STARTED: Время воспроизведения ограничено значениемSTARTEDво время смены сцен, например, при перемотке вперед или назад, или когда сцена закрыта наложением.
Для наложений , таких как диалоги или нижние панели:
-
RESUMED: Разрешено только для самой верхней, активной в данный момент сцены наложения. -
STARTED: Для всех нижележащих сцен наложения, которые перекрываются более новым наложением, установлено значениеSTARTED.
Начальное состояние жизненного цикла
Библиотека управляет максимальным состоянием жизненного цикла каждого отдельного элемента NavEntry в зависимости от его присутствия в стеке возврата:
-
RESUMED: Если запись присутствует в текущем стеке возврата, ее жизненный цикл может продолжаться доRESUMED(с учетом ограничения на уровне сцены). -
CREATED: Если элемент больше не находится в стеке возврата , например, когда он был удален, но все еще отображается на экране во время анимации, библиотека строго ограничивает его жизненный цикл значениемCREATED. Это ограничение гарантирует, что фоновые или завершающие выполнение элементы прекращают выполнение активной работы, такой как сбор потоков или запуск сопрограмм, привязанных к состояниямRESUMEDилиSTARTED, пока они завершают свои переходы при выходе.
Как они сочетаются
Например, окончательное состояние жизненного цикла элемента NavEntry определяется следующим образом:
| Сценарий | Ограничение уровня сцены | Начальный уровень | Эффективный лимит |
|---|---|---|---|
| Активный вход, фиксированный экран (без переходов и наложений) | RESUMED | RESUMED | RESUMED |
| Активный вход во время перехода (навигация в или из) | STARTED | RESUMED | STARTED |
| Активный вход, закрытый наложением (например, открыто диалоговое окно). | STARTED | RESUMED | STARTED |
| Всплывающее окно, анимированное завершение | STARTED или RESUMED | CREATED | CREATED |
Собираем всё воедино
На следующей диаграмме показано, как происходит обмен данными между различными объектами в Navigation 3:

События навигации инициируют изменения . Клавиши добавляются или удаляются из стека возврата в ответ на действия пользователя.
Изменение состояния стека возврата запускает процесс получения контента .
NavDisplay(композируемый объект, отображающий стек возврата) отслеживает состояние стека возврата. В конфигурации по умолчанию он отображает самую верхнюю запись стека возврата в однопанельном макете. Когда изменяется ключ верхнего элемента в стеке возврата,NavDisplayиспользует этот ключ для запроса соответствующего контента у поставщика записей.Поставщик элементов предоставляет контент . Поставщик элементов — это функция, которая преобразует ключ в элемент
NavEntry. Получив ключ отNavDisplay, поставщик элементов предоставляет связанный с нимNavEntry, содержащий как ключ, так и контент.Содержимое отображается .
NavDisplayполучаетNavEntryи отображает содержимое.