Transmitir dados entre destinos

O componente Navigation permite que você anexe dados a uma operação de navegação definindo argumentos para um destino. Por exemplo, um destino de perfil de usuário pode usar um argumento de ID do usuário para determinar qual usuário mostrar.

Em geral, recomendamos que você dê preferência para a transmissão da quantidade mínima de dados entre destinos. Por exemplo, transmita uma chave para extrair um objeto em vez de transmitir o próprio objeto, já que o espaço total para todos os estados salvos é limitado no Android. Se você precisar transmitir grandes quantidades de dados, use um ViewModel, conforme descrito na Visão geral do ViewModel.

Definir argumentos de destino

Para transmitir dados entre destinos, primeiro defina o argumento adicionando-o ao destino que o recebe seguindo estas etapas:

  1. No Navigation Editor, clique no destino que recebe o argumento.
  2. No painel Attributes, clique em Add (+).
  3. Na janela Add Argument Link que é exibida, insira o nome do argumento, o tipo dele, se ele é anulável e um valor padrão, se necessário.
  4. Clique em Adicionar. O argumento aparecerá na lista Arguments no painel Attributes.
  5. Clique na ação correspondente que leva você a esse destino. No painel Attributes, seu argumento recém-adicionado aparecerá na seção Argument Default Values.
  6. Veja que o argumento foi adicionado em XML. Clique na guia Text para alternar para a visualização XML e observe que seu argumento foi adicionado ao destino que o recebe. Confira um exemplo abaixo:

     <fragment android:id="@+id/myFragment" >
         <argument
             android:name="myArg"
             app:argType="integer"
             android:defaultValue="0" />
     </fragment>
    

Tipos de argumentos com suporte

A biblioteca "Navigation" é compatível com os seguintes tipos de argumento:

Tipo Sintaxe app:argType Compatibilidade com valores padrão Processado por rotas Nullable
Número inteiro app:argType="integer" Sim Sim Não
Flutuante app:argType="float" Sim Sim Não
Longo app:argType="long" Sim: os valores padrão precisam sempre terminar com um sufixo "L" (por exemplo, "123L"). Sim Não
Booleano app:argType="boolean" Sim: "verdadeiro" ou "falso" Sim Não
String app:argType="string" Sim Sim Sim
Referência de recursos app:argType="reference" Sim: os valores padrão precisam estar no formato "@resourceType/resourceName" (por exemplo, "@style/myCustomStyle") ou "0" Sim Não
Parcelable personalizado app:argType="<type>", em que <type> é o nome da classe totalmente qualificado do Parcelable Compatível com um valor padrão de "@null". Não é compatível com outros valores padrão. Não Sim
Serializável personalizado app:argType="<type>", em que <type> é o nome da classe totalmente qualificado do Serializable Compatível com um valor padrão de "@null". Não é compatível com outros valores padrão. Não Sim
Enumeração personalizada app:argType="<type>", em que <type> é o nome totalmente qualificado do tipo enumerado Sim: os valores padrão precisam corresponder ao nome não qualificado (por exemplo, "SUCCESS" para corresponder a MyEnum.SUCCESS). Não Não

Se um tipo de argumento for compatível com valores nulos, você poderá declarar um valor padrão de null usando android:defaultValue="@null".

Rotas, links diretos e URIs com argumentos podem ser analisados usando strings. Não é possível fazer isso usando tipos de dados personalizados, como Parcelables e Serializables, conforme mostrado na tabela anterior. Para transmitir dados complexos personalizados, armazene os dados em outro lugar, como um ViewModel ou banco de dados, e transmita apenas um identificador durante a navegação. Em seguida, extraia os dados no novo local após a conclusão da navegação.

Quando você escolhe um dos tipos personalizados, a caixa de diálogo Select Class é exibida e solicita que você escolha a classe correspondente para esse tipo. A guia Project permite escolher uma classe do projeto atual.

Escolha o <inferred type> para fazer com que a biblioteca "Navigation" determine o tipo com base no valor fornecido.

Marque Array para indicar que o argumento precisa ser uma matriz do valor de Type selecionado. Observe o seguinte:

  • Matrizes de enumerações e matrizes de referências de recursos não são compatíveis.
  • As matrizes oferecem suporte a valores anuláveis, independente do suporte a valores anuláveis do tipo em questão. Por exemplo, o uso de app:argType="integer[]" permite usar app:nullable="true" para indicar que transmitir uma matriz nula é aceitável.
  • As matrizes oferecem suporte a um único valor padrão, "@null". As matrizes não oferecem suporte a nenhum outro valor padrão.

Substituir um argumento de destino em uma ação

Argumentos e valores padrão no nível de destino são usados por todas as ações que navegam até o destino. Se necessário, modifique o valor padrão de um argumento (ou defina um, se ele ainda não existir), definindo um argumento no nível da ação. Esse argumento precisa ter o mesmo nome e tipo que o argumento declarado no destino.

O XML abaixo declara uma ação com um argumento que substitui o do nível de destino do exemplo anterior:

<action android:id="@+id/startMyFragment"
    app:destination="@+id/myFragment">
    <argument
        android:name="myArg"
        app:argType="integer"
        android:defaultValue="1" />
</action>

Usar o Safe Args para transmitir dados com a segurança de tipo

O componente Navigation tem um plug-in para Gradle chamado Safe Args que gera classes simples de objeto e builder para navegação com segurança de tipos e acesso a qualquer argumento associado. O Safe Args é altamente recomendado para a navegação e a transmissão de dados, porque garante a segurança de tipo.

Se você não estiver usando o Gradle, não será possível usar o plug-in Safe Args. Nessas situações, você pode usar Bundles para transmitir dados diretamente.

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.8.4"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Kotlin

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.8.4"
        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 vai conter as classes e métodos com segurança de tipos abaixo para cada ação, bem como para cada destino de envio e recebimento.

  • Uma classe é criada para cada destino no qual uma ação se origina. O nome dessa classe é o nome do destino de origem, anexado com a palavra "Directions". Por exemplo, se o destino de origem for um fragmento com o nome SpecifyAmountFragment, a classe gerada será chamada de SpecifyAmountFragmentDirections.

    Essa classe tem um método para cada ação definida no destino de origem.

  • Para cada ação usada para transmitir o argumento, é criada uma classe interna cujo nome vem da ação. Por exemplo, se a ação for chamada de confirmationAction,, a classe será denominada ConfirmationAction. Se a ação contiver argumentos sem defaultValue, use a classe de ação associada para definir o valor dos argumentos.

  • Uma classe é criada para o destino de recebimento. O nome dessa classe é o nome do destino, anexado com a palavra "Args". Por exemplo, se o fragmento de destino tiver o nome ConfirmationFragment,, a classe gerada será ConfirmationFragmentArgs. Use o método fromBundle() dessa classe para extrair os argumentos.

O exemplo abaixo mostra como usar esses métodos para definir um argumento e transmiti-lo ao método navigate():

Kotlin

override fun onClick(v: View) {
   val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
   val amount = amountTv.text.toString().toInt()
   val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
   v.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
   EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
   int amount = Integer.parseInt(amountTv.getText().toString());
   ConfirmationAction action =
           SpecifyAmountFragmentDirections.confirmationAction();
   action.setAmount(amount);
   Navigation.findNavController(view).navigate(action);
}

No código do destino de recebimento, use o método getArguments() para extrair o pacote e usar o conteúdo dele. Ao usar as dependências -ktx, os usuários do Kotlin também podem usar a delegação de propriedade by navArgs() para acessar argumentos.

Kotlin

val args: ConfirmationFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val tv: TextView = view.findViewById(R.id.textViewAmount)
    val amount = args.amount
    tv.text = amount.toString()
}

Java

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    TextView tv = view.findViewById(R.id.textViewAmount);
    int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
    tv.setText(amount + "");
}

Usar o Safe Args com uma ação global

Ao usar o Safe Args com uma ação global, é necessário fornecer um valor android:id para o elemento raiz <navigation>, como mostrado no exemplo a seguir:

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

    ...

</navigation>

O componente de navegação gera uma classe Directions para o elemento <navigation> com base no valor android:id. Por exemplo, se você tiver um elemento <navigation> com android:id=@+id/main_nav, a classe gerada terá o nome MainNavDirections. Todos os destinos no elemento <navigation> têm métodos gerados para acessar todas as ações globais associadas usando os mesmos métodos descritos na seção anterior.

Transmitir dados entre destinos com objetos Bundle

Se você não estiver usando o Gradle, ainda poderá transmitir argumentos entre destinos usando objetos Bundle. Crie um objeto Bundle e o transmita para o destino usando navigate(), como no exemplo abaixo:

Kotlin

val bundle = bundleOf("amount" to amount)
view.findNavController().navigate(R.id.confirmationAction, bundle)

Java

Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);

No código do destino de recebimento, use o método getArguments() para extrair o Bundle e usar o conteúdo:

Kotlin

val tv = view.findViewById<TextView>(R.id.textViewAmount)
tv.text = arguments?.getString("amount")

Java

TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));

Transmitir dados para o destino inicial

Você pode transmitir dados para o destino inicial do seu app. Primeiro, construa explicitamente um Bundle que armazene os dados. Em seguida, use uma das abordagens abaixo para transmitir o Bundle para o destino inicial:

Para extrair os dados no destino inicial, chame Fragment.getArguments().

Considerações sobre o ProGuard

Se você estiver reduzindo seu código, precisará impedir que os nomes de classes Parcelable, Serializable e Enum sejam ofuscados como parte do processo de minificação. Isso pode ser feito de duas maneiras:

  • Usando anotações @Keep.
  • Usando regras keepnames.

As subseções abaixo apresentam essas abordagens.

Usar anotações @Keep

O exemplo abaixo adiciona as anotações @Keep a definições de classe de modelo:

Kotlin

@Keep class ParcelableArg : Parcelable { ... }

@Keep class SerializableArg : Serializable { ... }

@Keep enum class EnumArg { ... }

Java

@Keep public class ParcelableArg implements Parcelable { ... }

@Keep public class SerializableArg implements Serializable { ... }

@Keep public enum EnumArg { ... }

Usar regras keepnames

Você também pode adicionar regras keepnames ao seu arquivo proguard-rules.pro, como mostrado no exemplo a seguir:

proguard-rules.pro

...

-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg

...

Outros recursos

Para saber mais sobre navegação, consulte os recursos adicionais a seguir.

Codelabs

Vídeos