Navegar até um destino

A navegação até um destino é feita usando um NavController, um objeto que gerencia a navegação do app dentro de um NavHost. Cada NavHost tem o próprio NavController correspondente. NavController fornece algumas maneiras diferentes de navegar até um destino, que são descritas mais detalhadamente nas seções abaixo.

Para recuperar NavController para um fragmento, uma atividade ou uma visualização, use um dos seguintes métodos:

Kotlin:

Java:

Depois de recuperar um NavController, você pode chamar uma das sobrecargas de navigate() para navegar entre destinos. Cada sobrecarga é compatível com vários cenários de navegação, conforme descrito nas seções a seguir.

Usar o Safe Args para navegar com a segurança de tipo

A maneira recomendada de navegar entre destinos é usar o plug-in Safe Args do Gradle. Esse plug-in gera classes de objetos e builders simples que permitem a navegação segura de tipos entre destinos. O Safe Args é altamente recomendado para a navegação e a transmissão de dados entre destinos.

Para adicionar Safe Args ao seu projeto, inclua o seguinte classpath no seu arquivo build.gradle de nível superior:

Groovy

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.5.3"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Kotlin

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.5.3"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

Você também precisa aplicar um dos dois plug-ins disponíveis.

Para gerar um código de linguagem Java adequado para módulos Java ou Java e Kotlin mistos, adicione esta linha ao arquivo build.gradle do seu app ou módulo:

Groovy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs")
}

Como alternativa, para gerar o código Kotlin adequado para módulos somente Kotlin, adicione:

Groovy

plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

Você precisa ter android.useAndroidX=true no arquivo gradle.properties, como mostrado em Migrar para o AndroidX.

Depois de ativar o Safe Args, o código gerado contém classes e métodos para cada ação definida, bem como classes correspondentes a cada destino de envio e destino.

O Safe Args gera uma classe para cada destino de origem de uma ação. O nome da classe gerada adiciona "Directions" ao nome da classe de destino de origem. Por exemplo, se o destino de origem tem o nome de SpecifyAmountFragment, a classe gerada será chamada SpecifyAmountFragmentDirections.

A classe gerada contém um método estático para cada ação definida no destino de origem. Esse método usa qualquer parâmetro de ação definido como argumentos e retorna um objeto NavDirections que você pode transmitir diretamente para navigate().

Exemplo de Safe Args

Como exemplo, vamos supor que temos um gráfico de navegação com uma única ação que conecta dois destinos, SpecifyAmountFragment e ConfirmationFragment. O ConfirmationFragment usa um único parâmetro float fornecido como parte da ação.

O Safe Args gera uma classe SpecifyAmountFragmentDirections com um único método, actionSpecifyAmountFragmentToConfirmationFragment(), e uma classe interna chamada ActionSpecifyAmountFragmentToConfirmationFragment. A classe interna é derivada de NavDirections e armazena o ID de ação associado e o parâmetro float. O objeto NavDirections retornado pode ser transmitido diretamente para navigate(), conforme mostrado no exemplo a seguir.

Kotlin

override fun onClick(v: View) {
    val amount: Float = ...
    val action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment(amount)
    v.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
    float amount = ...;
    action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment(amount);
    Navigation.findNavController(view).navigate(action);
}

Para ver mais informações sobre como transmitir dados entre destinos com o Safe Args, consulte Usar o Safe Args para transmitir dados com segurança de tipo.

Navegar usando ID

navigate(int) usa o ID do recurso de uma ação ou um destino. O snippet de código a seguir mostra como navegar até ViewTransactionsFragment.

Kotlin

viewTransactionsButton.setOnClickListener { view ->
   view.findNavController().navigate(R.id.viewTransactionsAction)
}

Java

viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
    }
});

Para os botões, você também pode usar o método de conveniência createNavigateOnClickListener() da classe Navigation para navegar até um destino, conforme mostrado no exemplo a seguir.

Kotlin

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null))

Java

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));

Para gerenciar com outros componentes comuns de IU, por exemplo, a barra de apps superior e a navegação inferior, consulte Atualizar componentes de IU com NavigationUI.

Quando você define uma ação no gráfico de navegação, a navegação gera uma classe NavAction correspondente, que contém as configurações definidas para essa ação, incluindo:

  • Destino: o ID do recurso do destino.
  • Argumentos padrão: um android.os.Bundle contendo valores padrão para o destino, se fornecido.
  • Opções de navegação: opções de navegação, representadas como NavOptions. Essa classe contém toda a configuração especial para a transição para e de volta do destino, incluindo a configuração do recurso de animação, o comportamento de destaque e se o destino precisa ser iniciado no modo superior único.

Vamos dar uma olhada em um gráfico de exemplo que consiste em duas telas com uma ação para navegar de uma para outra:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/nav_graph"
            app:startDestination="@id/a">

    <fragment android:id="@+id/a"
              android:name="com.example.myapplication.FragmentA"
              android:label="a"
              tools:layout="@layout/a">
        <action android:id="@+id/action_a_to_b"
                app:destination="@id/b"
                app:enterAnim="@anim/nav_default_enter_anim"
                app:exitAnim="@anim/nav_default_exit_anim"
                app:popEnterAnim="@anim/nav_default_pop_enter_anim"
                app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
    </fragment>

    <fragment android:id="@+id/b"
              android:name="com.example.myapplication.FragmentB"
              android:label="b"
              tools:layout="@layout/b">
        <action android:id="@+id/action_b_to_a"
                app:destination="@id/a"
                app:enterAnim="@anim/nav_default_enter_anim"
                app:exitAnim="@anim/nav_default_exit_anim"
                app:popEnterAnim="@anim/nav_default_pop_enter_anim"
                app:popExitAnim="@anim/nav_default_pop_exit_anim"
                app:popUpTo="@+id/a"
                app:popUpToInclusive="true"/>
    </fragment>
</navigation>

Quando o gráfico de navegação é aumentado, essas ações são analisadas, e os objetos NavAction correspondentes são gerados com as configurações definidas no gráfico. Por exemplo, action_b_to_a é definido como a navegação do destino b para o destino a. A ação inclui animações com o comportamento popTo que remove todos os destinos da pilha de retorno. Todas essas configurações são capturadas como NavOptions e anexadas a NavAction.

Para acompanhar esse NavAction, use NavController.navigate(), passando o ID da ação, conforme mostrado no exemplo a seguir.

Kotlin

findNavController().navigate(R.id.action_b_to_a)

Java

NavigationHostFragment.findNavController(this).navigate(R.id.action_b_to_a);

Navegar usando DeepLinkRequest

Você pode usar navigate(NavDeepLinkRequest) para navegar diretamente para um destino de link direto implícito, conforme mostrado no exemplo a seguir.

Kotlin

val request = NavDeepLinkRequest.Builder
    .fromUri("android-app://androidx.navigation.app/profile".toUri())
    .build()
findNavController().navigate(request)

Java

NavDeepLinkRequest request = NavDeepLinkRequest.Builder
    .fromUri(Uri.parse("android-app://androidx.navigation.app/profile"))
    .build()
NavHostFragment.findNavController(this).navigate(request)

Além de Uri, NavDeepLinkRequest também é compatível com links diretos com ações e tipos MIME. Para adicionar uma ação à solicitação, use fromAction() ou setAction(). Para adicionar um tipo MIME a uma solicitação, use fromMimeType() ou setMimeType().

Para que um NavDeepLinkRequest corresponda corretamente a um destino de link direto implícito, o URI, a ação e o tipo MIME precisam corresponder a NavDeepLink no destino. Os URIs precisam corresponder ao padrão, as ações precisam ser uma correspondência exata e os tipos MIME precisam estar relacionados. Por exemplo, correspondências "imagem/jpg" com "imagem/*".

Ao contrário da navegação usando IDs de ação ou de destino, você pode navegar para qualquer link direto no seu gráfico, independentemente de o destino estar visível ou não. Você pode navegar para um destino no gráfico atual ou para um destino em um gráfico completamente diferente.

Ao navegar usando NavDeepLinkRequest, a pilha de retorno não é redefinida. Esse comportamento é diferente de outras navegações de link direto, em que a pilha de retorno é substituída durante a navegação. popUpTo e popUpToInclusive ainda removem destinos da pilha de retorno como se você tivesse navegado usando um ID.

Navegação e a pilha de retorno

O Android mantém uma pilha de retorno que contém os destinos visitados. O primeiro destino do seu app é colocado na pilha quando o usuário abre o app. Cada chamada para o método navigate() coloca outro destino no topo da pilha. Tocar em Up ou Back chama os métodos NavController.navigateUp() e NavController.popBackStack(), respectivamente, para remover (ou retirar) o destino superior da pilha.

NavController.popBackStack() retorna um valor booleano que indica se ele retornou para outro destino. O caso mais comum de retorno de false é quando você destaca manualmente o destino inicial do seu gráfico.

Quando o método retorna false, NavController.getCurrentDestination() retorna null. Você é responsável por navegar para um novo destino ou por manipular o menu pop-up chamando finish() na sua atividade, conforme mostrado no exemplo a seguir.

Kotlin

...

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

Java

...

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

popUpTo e popUpToInclusive

Ao navegar usando uma ação, você também pode retirar destinos adicionais da pilha de retorno. 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 removidos da pilha de retorno para que o botão" "Voltar" não retorne os usuários ao fluxo de login.

Para inserir destinos ao navegar de um destino para outro, adicione um atributo app:popUpTo ao elemento <action> associado. app:popUpTo instrui a biblioteca de navegação a remover alguns destinos da pilha de retorno como parte da chamada para navigate(). O valor do atributo é o ID do destino mais recente que precisa permanecer na pilha.

Você também pode incluir app:popUpToInclusive="true" para indicar que o destino especificado em app:popUpTo também precisa ser removido da pilha de retorno.

Exemplo de popUpTo: lógica circular

Digamos que seu app tenha três destinos (A, B e C), juntamente com ações que levam de A a B, B a C e C de volta a A. O gráfico de navegação correspondente é mostrado na Figura 1:

Figura 1. Um gráfico de navegação circular com três destinos: A, B e C.

Em cada ação de navegação, um destino é adicionado à pilha de retorno. Se você navegasse várias vezes por esse fluxo, sua pilha de retorno conteria vários conjuntos de cada destino (A, B, C, A, B, C, A e assim por diante). Para evitar essa repetição, você pode especificar app:popUpTo e app:popUpToInclusive na ação que leva do destino C para o destino A, conforme mostrado no exemplo a seguir.

<fragment
    android:id="@+id/c"
    android:name="com.example.myapplication.C"
    android:label="fragment_c"
    tools:layout="@layout/fragment_c">

    <action
        android:id="@+id/action_c_to_a"
        app:destination="@id/a"
        app:popUpTo="@+id/a"
        app:popUpToInclusive="true"/>
</fragment>

Depois de chegar ao destino C, a pilha de retorno contém uma instância de cada destino (A, B, C). Ao navegar de volta ao destino A, também popUpTo A, o que significa que removemos B e C da pilha ao navegar. Com app:popUpToInclusive="true", também retiramos primeiro A da pilha, apagando-o. Se você não usar app:popUpToInclusive, a pilha de retorno terá duas instâncias do destino A.