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

Создайте обратную стопку
В Navigation 3 стек возвратов фактически не содержит контент. Вместо этого он содержит ссылки на контент , известные как ключи . Ключи могут быть любого типа, но обычно представляют собой простые сериализуемые классы данных. Использование ссылок вместо контента имеет следующие преимущества:
- Навигация осуществляется простым нажатием клавиш на задней панели.
- Пока ключи сериализуемы, стек возвратов можно сохранить в постоянном хранилище, что позволяет ему пережить изменения конфигурации и смерть процесса. Это важно, поскольку пользователи ожидают, что покинут ваше приложение, вернутся к нему позже и продолжат с того места, где остановились, с тем же отображаемым содержимым. Подробнее см. в разделе Сохраните стек возвратов .
Ключевая концепция Navigation 3 API заключается в том, что вы являетесь владельцем back stack. Библиотека:
- Ожидает, что ваш back stack будет
List<T>
поддерживаемым snapshot-state , гдеT
— типkeys
back stack . Вы можете использовать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
, создайте entryProvider
. Это функция, которая принимает key
и возвращает NavEntry
для этого key
. Обычно он определяется как лямбда-параметр при создании NavDisplay
.
Существует два способа создания entryProvider
: либо путем непосредственного создания лямбда-функции, либо с использованием entryProvider
DSL.
Создайте функцию entryProvider
напрямую
Обычно вы создаете функцию entryProvider
с помощью оператора 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") } } } }
Используйте entryProvider
DSL
entryProvider
DSL может упростить вашу лямбда-функцию, избежав необходимости проверки каждого из типов ключей и создания 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 представляет состояние навигации вашего приложения. Всякий раз, когда back stack изменяется, пользовательский интерфейс приложения должен отражать новое состояние back stack. В Navigation 3 NavDisplay
наблюдает за вашим back stack и обновляет свой пользовательский интерфейс соответствующим образом. Создайте его со следующими параметрами:
- Ваш стек возврата - это должен быть тип
SnapshotStateList<T>
, гдеT
- это тип ключей вашего стека возврата. Это наблюдаемыйList
, так что он запускает перекомпоновкуNavDisplay
при его изменении. -
entryProvider
для преобразования ключей в вашем стеке переходов вNavEntry
. - При желании можно указать лямбду для параметра
onBack
. Она вызывается, когда пользователь запускает событие back.
В следующем примере показано, как создать 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
по умолчанию с двумя пунктами назначения.Собираем все вместе
На следующей диаграмме показано, как данные передаются между различными объектами в Navigation 3:

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