Понять и внедрить основы

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

Моделирование состояния навигации

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

Кнопка действия программной клавиатуры (значок галочки), обведенная красным.
Рисунок 1. Диаграмма, показывающая, как изменяется стек переходов в зависимости от событий навигации пользователя.

Создайте обратную стопку

В 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 .

Существует два способа создания поставщика данных: либо путем непосредственного создания лямбда-функции, либо с использованием entryProvider DSL.

Создайте функцию 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") }
        }
    }
}

Используйте entryProvider DSL

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

Отобразить стек назад

Стек переходов представляет собой состояние навигации вашего приложения. При каждом изменении стека переходов пользовательский интерфейс приложения должен отражать новое состояние. В 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 по умолчанию с двумя пунктами назначения.
Рисунок 2. Поведение NavDisplay по умолчанию с двумя пунктами назначения.

Собираем все вместе

На следующей диаграмме показано, как данные передаются между различными объектами в Navigation 3:

Визуализация потоков данных между различными объектами в Navigation 3.
Рисунок 3. Диаграмма, показывающая, как данные проходят через различные объекты в Navigation 3.
  1. События навигации инициируют изменения . Ключи добавляются или удаляются из стека переходов в ответ на действия пользователя.

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

  3. Поставщик записи предоставляет контент . Поставщик записи — это функция, которая преобразует ключ в NavEntry . Получив ключ от NavDisplay , поставщик записи предоставляет связанный NavEntry , содержащий как ключ, так и контент.

  4. Содержимое отображается . NavDisplay получает NavEntry и отображает содержимое.