Navegação e a backstack

O NavController tem uma "backstack" que contém os destinos que o usuário visitou. À medida que o usuário navega para outras telas no app, o NavController adiciona destinos à backstack e os remove dela.

Dentro da pilha, a backstack é uma estrutura de dados "último a chegar, primeiro a sair". Portanto, o NavController envia itens para a pilha e exibe os itens da parte de cima dela.

Comportamento básico

Estes são os principais fatos que você precisa considerar sobre o comportamento da backstack:

  • Primeiro destino: quando o usuário abre o app, o NavController envia o primeiro destino para a parte de cima da backstack.
  • Enviar para a pilha: cada chamada NavController.navigate() envia o destino especificado para a parte de cima da pilha.
  • Abrir o destino superior: tocar em Up ou Back chama os métodos NavController.navigateUp() e NavController.popBackStack(), respectivamente. Eles retiram o destino superior da pilha. Consulte a página Princípios de navegação para mais informações sobre a diferença entre Up e Back.

Retornar

O método NavController.popBackStack() tenta retirar o destino atual da backstack e navegar para o destino anterior. Isso faz com que o usuário volte uma etapa no histórico de navegação. O método retorna um booleano indicando se ele retornou para o destino.

Retornar para um destino específico

Você também pode usar popBackStack() para navegar a um destino específico. Para isso, use uma das sobrecargas dele. Há várias que permitem transmitir um identificador, como um id inteiro ou uma string route. Essas sobrecargas levam o usuário ao destino associado ao identificador fornecido. Elas destacam tudo que está acima desse destino na pilha.

Essas sobrecargas também usam um booleano inclusive. Ele determina se o NavController também precisa retirar o destino especificado da backstack depois de ter navegado até ele.

Confira este breve exemplo:

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

Aqui o NavController retorna ao destino com o ID de número inteiro destinationId. Como o valor do argumento inclusive é true, o NavController também exibe o destino especificado da backstack.

Processar um retorno com falha

Quando popBackStack() retornar false, uma chamada subsequente para NavController.getCurrentDestination() retornará null. Isso significa que o app retirou o último destino da backstack. Nesse caso, o usuário recebe apenas uma tela em branco.

Isso pode ocorrer nos seguintes casos:

  • popBackStack() não exibiu nada da pilha.
  • popBackStack() retirou um destino da backstack, e a pilha agora está vazia.

Para resolver isso, navegue até um novo destino ou chame finish() na atividade para encerrar. O snippet a seguir demonstra isso:

kotlin

...

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

java

...

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

Abrir um destino

Para remover destinos da backstack ao navegar de um destino para outro, adicione um argumento popUpTo() à chamada de função navigate() associada. popUpTo() instrui a biblioteca de navegação a remover alguns destinos da backstack como parte da chamada para navigate(). O valor do parâmetro é o identificador de um destino na backstack. O identificador pode ser um id inteiro ou uma string route.

Você pode incluir um argumento para o parâmetro inclusive com um valor de true para indicar que o destino especificado em popUpTo() também deve ser retirado da backstack.

Para implementar isso de forma programática, transmita popUpTo() para navigate() como parte de NavOptions com inclusive definido como true. Isso funciona no Compose e no Views.

Salvar o estado ao usar a opção de abrir um destino

Ao usar popUpTo para navegar até um destino, você pode, opcionalmente, salvar os estados de todos os destinos retirados da backstack.

Para ativar essa opção, defina popUpToSaveState como true na action associada ou chame para NavController.navigate().

Ao navegar até um destino, você também pode definir restoreSaveState como true para restaurar automaticamente o estado associado ao destino na propriedade destination.

Exemplo em XML

Confira um exemplo de popUpTo em XML com uma ação:

<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"/>

Exemplos no Compose

Confira a seguir um exemplo completo do mesmo recurso no Compose:

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: String = "destination_a"
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable("destination_a") {
            DestinationA(
                onNavigateToB = {
                // 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") {
                            inclusive = true
                            saveState = true
                        }
                    }
                },
            )
        }
        composable("destination_b") { DestinationB(/* ... */) }
    }
}

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

Mais detalhadamente, você pode mudar a forma como chama NavController.navigate() das seguintes maneiras:

// 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
}

Para informações gerais sobre como transmitir opções para NavController.navigate(), consulte o Guia de navegação com opções.

Destacar usando ações

Ao navegar usando uma ação, você também pode retirar destinos adicionais da backstack. Por exemplo, se o app tiver um fluxo de login inicial, assim que um usuário tiver feito login, todos os destinos relacionados ao login precisarão ser retirados da backstack para que o botão" "Voltar" não retorne os usuários ao fluxo de login.

Leitura adicional

Para mais informações, confira as seguintes páginas:

  • Navegação circular: aprenda a evitar uma backstack sobrecarregada nos casos em que os fluxos de navegação são circulares.
  • Destinos da caixa de diálogo: leia sobre como os destinos da caixa de diálogo introduzem considerações exclusivas sobre como gerenciar a backstack.