Навигация и задний стек

NavController содержит "стек возврата", в котором хранятся посещенные пользователем страницы. По мере того, как пользователь перемещается между экранами вашего приложения, NavController добавляет и удаляет страницы из стека возврата.

Поскольку стек является стеком, его содержимое представляет собой структуру данных по принципу «последний вошел — первый вышел». Поэтому NavController добавляет элементы в стек и извлекает их из него.

Базовое поведение

Вот основные факты, которые следует учитывать при работе со стеком возврата:

  • Первый пункт назначения: Когда пользователь открывает приложение, NavController помещает первый пункт назначения на вершину стека возврата.
  • Добавление в стек: Каждый вызов NavController.navigate() добавляет указанный пункт назначения на вершину стека.
  • Удаление верхнего пункта назначения: Нажатие кнопок «Вверх» или «Назад» вызывает методы NavController.navigateUp() и NavController.popBackStack() соответственно. Они удаляют верхний пункт назначения из стека. Дополнительную информацию о различиях между кнопками «Вверх» и «Назад» см. на странице «Принципы навигации».

Вернуться назад

Метод NavController.popBackStack() пытается удалить текущий пункт назначения из стека возврата и перейти к предыдущему пункту назначения. Это фактически возвращает пользователя на один шаг назад в истории навигации. Метод возвращает логическое значение, указывающее, успешно ли выполнен возврат к пункту назначения.

Вернуться в определенное место назначения

Вы также можете использовать popBackStack() для перехода к определенному пункту назначения. Для этого используйте одну из его перегрузок. Существует несколько перегрузок, позволяющих передавать идентификатор, например, целочисленный id или строковый route . Эти перегрузки переводят пользователя к пункту назначения, связанному с заданным идентификатором. Важно отметить, что они удаляют из стека все элементы, находящиеся выше этого пункта назначения.

Эти перегрузки также принимают логическое значение, inclusive . Оно определяет, следует ли NavController также удалять указанный пункт назначения из стека возврата после перехода к нему.

Рассмотрим этот короткий фрагмент в качестве примера:

navController.popBackStack(R.id.destinationId, true)

В данном случае NavController возвращается к месту назначения с целочисленным идентификатором destinationId . Поскольку значение аргумента inclusive равно true , NavController также удаляет указанное место назначения из стека возврата.

Обработка неудачной попытки возврата на место.

Когда метод popBackStack() возвращает false , последующий вызов NavController.getCurrentDestination() возвращает null . Это означает, что приложение удалило последний пункт назначения из стека возврата. В этом случае пользователь видит только пустой экран.

Это может произойти в следующих случаях:

  • popBackStack() ничего не извлекла из стека.
  • popBackStack() извлекла целевой объект из стека возврата, и теперь стек пуст.

Для решения этой проблемы необходимо перейти к новому пункту назначения или вызвать finish() для завершения работы активности. Следующий фрагмент кода демонстрирует это:

котлин

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

java

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

Всплывающее окно в пункт назначения

Чтобы удалить пункты назначения из стека возврата при переходе от одного пункта назначения к другому, добавьте аргумент popUpTo() к соответствующему вызову функции navigate() . Функция popUpTo() указывает библиотеке Navigation удалить некоторые пункты назначения из стека возврата в рамках вызова функции navigate() . Значение параметра — это идентификатор пункта назначения в стеке возврата. Идентификатор может быть целочисленным значением id или строкой route .

В качестве аргумента для параметра inclusive можно указать значение true , чтобы обозначить, что указанное вами в popUpTo() место назначения также должно быть удалено из стека возврата.

Для программной реализации передайте popUpTo() в navigate() как часть NavOptions , установив inclusive в значение true . Это работает как в Compose, так и в Views.

Сохранять состояние при появлении всплывающего окна

При использовании popUpTo для перехода к нужному пункту назначения вы можете дополнительно сохранить стек возврата и состояния всех пунктов назначения, удаленных из стека возврата. Затем вы можете восстановить стек возврата и пункты назначения при последующем переходе к этому пункту назначения. Это позволяет сохранить состояние для данного пункта назначения и иметь несколько стеков возврата .

Чтобы сделать это программно, укажите saveState = true при добавлении popUpTo в параметры навигации.

Вы также можете указать restoreState = true в параметрах навигации, чтобы автоматически восстановить стек возврата и состояние, связанное с пунктом назначения.

Например:

navController.navigate(
    route = route,
    navOptions =  navOptions {
        popUpTo<A>{ saveState = true }
        restoreState = true
    }
)

Чтобы разрешить сохранение и восстановление состояния в XML, определите popUpToSaveState как true и restoreState как true соответственно в соответствующем action .

Пример XML

Вот пример вызова popUpTo в XML с использованием действия:

<action
  android:id="@+id/action_a_to_b"
  app:destination="@id/b"
  app:popUpTo="@+id/a"
  app:popUpToInclusive="true"
  app:restoreState=”true”
  app:popUpToSaveState="true"/>

Пример составления текста

Ниже приведён полный пример того же самого в Compose:

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: Any = A
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable<A> {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to, and including, the A destination off
                // the back stack, saving the back stack and the state of its
                // destinations.
                // Then restore any previous back stack state associated with
                // the B destination.
                // Finally navigate to the B destination.
                    navController.navigate(route = B) {
                        popUpTo<A> {
                            inclusive = true
                            saveState = true
                        }
                        restoreState = true
                    }
                },
            )
        }
        composable<B> { DestinationB(/* ... */) }
    }
}

@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
    Button(onClick = onNavigateToB) {
        Text("Go to A")
    }
}

Более детально, вы можете изменить способ вызова метода NavController.navigate() следующими способами:

// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a")
}

// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a") { inclusive = true }
}

// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
    launchSingleTop = true
}

Общую информацию о передаче параметров в метод NavController.navigate() см. в руководстве по навигации с использованием параметров .

Вывод с помощью действий

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

Дополнительная литература

Для получения более подробной информации ознакомьтесь со следующими страницами:

  • Циклическая навигация : узнайте, как избежать переполнения стека возврата в случаях, когда потоки навигации имеют циклический характер.
  • Направления диалогов : Узнайте о том, как направления диалогов вносят уникальные особенности в управление стеком возврата.