Concrete Logo
Hamburger button

Navigation Architecture Component

  • Blog
  • 27 de Fevereiro de 2019

Você sabe que é possível melhorar o fluxo de navegação quando trabalhamos com fragments? Pois é, no Google I/O 2018 a gigante mostrou um novo framework, chamado de Navigation Architecture Component, que faz parte da coleção de componentes Android Jetpack e pode nos ajudar nessa função. Antes de falar sobre o Navigation, porém, vamos dar uma introdução sobre o que é o Android Jetpack.  A Izabela Araújo já falou sobre o assunto aqui no Blog neste post se você quiser saber mais, mas basicamente o Android JetPack é um conjunto de componentes que ajudam o desenvolvedor a criar uma aplicação Android com algumas facilidades e a seguir algumas boas práticas, que vão simplificar o desenvolvimento.

O Android Jetpack é composto também por bibliotecas do pacote androidx.* e cada componente dele pode ser utilizado separadamente. Isso quer dizer que tudo que existe no Android Jetpack é novo? Bem…. a resposta mais simples para essa pergunta é não, ele apenas unificou os componentes do support library e Architecture components e organizou em quatro categorias:

 

Neste post vamos falar sobre o Navigation, que faz parte da categoria de Architecture no JetPack. Bora lá?

Navigation

A navegação de um app é uma questão crucial para a experiência do usuário, e o Navigation pode te ajudar bastante na hora de estruturar seu app.

Para trabalhar com Navigation é recomendado que se use o padrão de projeto chamado Single Activity. Além de facilitar a implementação, ele ajuda a visualizar como vai ficar todo o fluxo de navegação no Navigation Graph e não precisamos nos preocupar com os fluxos de add/replace ou commit do FragmentManager ou o controle da back stack. O Navigation é composto basicamente por componentes como Navigation Graph, Destinations, NavHost, NavController e NavigationUI.

Para utilizar o Navigation no seu projeto com Kotlin basta adicionar duas dependências no Gradle do app. São elas:

Navigation Graph

O Navigation Graph é um resource feito em XML no qual temos também a parte visual (Design) para verificar como está a navegação do app. Ele organiza todos os Destinations, actions e arguments que serão transitados.

Para criar um Navigation Graph vá até o diretório res, clique com o botão direito e siga o caminho New -> Android Resource File. Depois, selecione Navigation em Resource type.

Navigation Graph – Design

Observe a imagem abaixo. No ponto 1 está localizado o NavHost, que nesse exemplo vai ser a activity que vamos configurar depois. No ponto 2 estão todos os Destinations que vamos criar depois.

 

Navigation Graph – Texto

Destinations

Um Destination é basicamente qualquer tela no seu app que vai ter alguma interação com o usuário, ou seja, pode ser um fragment, uma activity ou até mesmo outros navigations graphs. Uma recomendação da Google é que se use fragments como destination para deixar clara a ideia do padrão de Single Acitvity.

Existe algumas formas de você adicionar seu fragment ou activity como destination no Navigation Graph. Uma das mais práticas é utilizar o modo de Design do Navigation Graph, no qual ele lista todos os seus fragments/activitys e você só seleciona o fragment/activity que quiser.

 

Outra forma de adicionar é a partir do modo de Text:

Uma informação bastante importante na hora de criar os Destinations é definir qual vai ser o startDestination, ou seja, o ponto de entrada do seu navigation. Para isso basta adicionar a seguinte tag no seu navigation:

 

Ou, pelo modo de Design, a partir dos atributos do navigation defina qual vai ser o startDestination.

Actions

Beleza! Agora já sei como criar destinations, mas como eu faço para navegar entre elas?

Então! A navegação entre destinations é feita por meio de um recurso chamado Actions. E também existem várias formas de criar uma action para navegar entre as destinations, umas delas é a partir do XML no modo Text.

No elemento de action existe uma propriedade chamada app:destination, na qual vamos definir qual vai ser o fragment que vamos navegar.

Ou, a partir do modo Design, arraste da origem para o destino.

No Navigation existe a possibilidade de criar uma Action de duas formas:

A primeira delas é uma action específica de um destination para outro, na qual apenas o destination de origem tem acesso. A segunda é uma action global, que é criada no escopo do navigation e todos os destinations têm acesso a ela.

As actions são responsáveis por configurar animações na transição entre destinations.

app:enterAnim e app:exitAnim: animações que são executadas no momento da transição para o próximo destination;
app:popEnterAnim e app:popExitAnim: znimações que são executadas no momento em que o destination é fechado.

Além das configurações básicas, você pode também configurar para trabalhar com Shared element transitions.

NavHost

O NavHost é basicamente uma activity que serve como um container para a troca dos fragments. Para sua activity se tornar um NavHost basta adicionar o elemento de fragment com algumas modificações.

android:name – define qual vai ser o componente do NavHost utilizado. Por padrão é o androidx.navigation.fragment.NavHostFragment.

app:defaultNavHost – quando esse atributo é definido como true, estamos informando que o NavHostFragment vai capturar e tratar o evento de Back Button. Você pode fazer esse tratamento manualmente também sobrescrevendo o método onSupportNavigateUp.

app:navGraph – define qual vai ser o arquivo de navGraph. Quando definimos esse valor ele automaticamente já reconhece essa activity como NavHost lá no nosso NavGraph.

NavController

Como o próprio nome já diz, o NavController é responsável por controlar todas as ações do nosso NavHostFragment. Ele trata ações como a manutenção da backstack e o comportamento da action bar, realiza a troca dos destinations no nosso navHost e ainda faz todo o trabalho de gerenciamento de fragments. Basicamente, quase todas as configurações que gostaríamos de fazer no nosso Navigation o responsável por isso vai ser o NavController.

Para acessarmos o NavController na Activity que foi marcada como Host basta fazer um cast no nosso componente de fragment e acessar a propriedade chamada navController:

A partir daí você já começa a brincar um pouco com as funcionalidades que o NavController oferece.

NavigationUI

A NavigationUI é uma classe com um conjunto de métodos do Navigation que auxilia e facilita o desenvolvimento junto com Top App bar, o Navigation Drawer e Bottom Navigation.

Podemos acessar diretamente os métodos estáticos do NavigationUI ou usar as extensions functions que existem dentro do seu pacote, como por exemplo setupActionBarWithNavController(), que vai executar um método estático chamado NavigationUI.setupActionBarWithNavController().

Neste exemplo, vamos usar algumas outras extensions functions, como a setupWithNavController( e navigateUp().

Configurando a ActionBar

Para utilizar o suporte do navigation na ActionBar vamos usar uma classe chamada AppBarConfiguration. Esta classe é a responsável por manter o comportamento da ActionBar para mostrar ou não a seta de back, por exemplo. Essa configuração é feita inicialmente com ajuda de um recurso chamado top-level destinations, que  são basicamente as telas principais do seu navigation ou o startDestination do seu Navigation Graph. Mais pra frente vamos ver como criar uma AppBarConfiguration na qual podemos aplicar quais serão as top-level destinations da nossa navegação.

Nesse primeiro momento, vamos usar o próprio startDestination como top-level destination da nossa ActionBar. Para fazer essa configuração basta criar um objeto do tipo AppBarConfiguration, que recebe como parâmetro inicial o navGraph do nosso NavHost.

Depois de criar o nosso AppBarConfiguration o próximo passo é aplicá-lo no nosso ActionBar a partir de uma extensions function chamada setupActionBarWithNavController. Essa extension function recebe dois parâmetros, o navController e o appBarConfiguration.

Por fim, sobrescrevemos o método onSupportNavigationUp para adicionarmos o controle do back ao nosso navController. Para isso vamos usar duas extensions functions: a findNavController, que vai servir para acessarmos o navController do nosso NavHost, e a navigateUp, na qual vamos passar o nosso appBarConfiguration.

Configurando a BottomNavigation

Para configurar a nossa BottomNavigation primeiramente vamos definir quais vão ser as nossas top-level destinations, ou seja, os fragments que são correspondentes a cada item do menu do nosso BottomNavigation.

BottomNavigation da nossa activity

 

Menu da nossa BottomNavigation

Para configurar quais são os top-level destinations vamos acessar a classe de Builder do AppBarConfiguration. O primeiro parâmetro que ele espera é um Set de ID’s, que são referentes aos fragments que foram criados no nosso NavGraph. Depois disso, bastar chamar o método de build que vai retornar uma instância do appBarConfiguration.

Depois, acesse a extension function criada para integrar o navController com o bottomNavigation, que é chamada setupWithNavController e na qual vamos passar o navController do nosso navHost.

Navegando entre Destinations

Para navegar entre as destinations vamos usar os métodos da classe de NavController.

Por padrão usamos um método chamado navigate, que recebe como parâmetro um resource ID no qual podemos passar o ID da action criada no nosso Navigation Graph ou passar o id do destination que queremos navegar. Neste método podemos passar também alguns parâmetros além do ID de destino, como por exemplo quais serão os argumentos que vamos enviar para o destination ou até mesmo aplicar um shared element transitions na transição entre as telas.

Para acessar o método de navigate existe uma extension function chamada findNavController que retorna o NavController do nosso NavHost. Neste exemplo eu criei um botão na tela de fragment_a que, se clicado, executa o navigate passando o ID da action criada dentro do nosso Navigation Graph.

Utilizando o SafeArgs

O SafeArgs é um plugin do Gradle usado para passar dados entre destinations. Com ele, não precisamos mais nos preocupar em criar Bundles e keys no nosso código para passar dados entre fragments, porque por baixo do panos ele gera alguns objetos e classes em um modo type-safe e faz todo esse trabalho por nós, desenvolvedores.

Para utilizar o SafeArgs em nosso projeto vamos ter que adicionar no build.gradle o seguinte classpath:

E no build.gradle do app vamos adicionar o seguinte apply:

O componente argument define os argumentos esperados no destination.

Vamos criar o primeiro argument dentro do component de destination do nosso AFragment no Navigation Graph:

Neste exemplo, estou criando um argumento chamado testValue que vai ser do tipo integer e vai receber um valor default 0.

Quando criamos um argumento e queremos que ele seja passado no momento da chamada da action, devemos criá-lo dentro do componente de action com o mesmo android:name e app:argType.

Na classe AFragment no click do botão acessamos uma das classes geradas pelo safeArgs chamada de AFragmentDirections. Vamos acessar o método gerado, chamado de action_AFragment_to_internalFragment2, que é o ID da nossa action. No fragment, onde o argumento está sendo implementado, o safeArgs sempre gera uma classe com o nome do fragment+Directions.

Nesse caso, como criamos um argumento no qual existia um valor default, a passagem dele por parâmetro fica opcional, caso não existisse o dev seria obrigado a imputar esse valor.

Na classe de destino, para recuperarmos esse argumento, vamos utilizar um delegate chamado navArgs.

Para isso, vamos criar um objeto do tipo AFragmentArgs, e essa classe gerada pelo safeArgs é responsável por criar os objetos relacionados aos argumentos que criamos no nosso destination.

Neste exemplo vou exibir o argumento que criamos no log:

Nested navigation graphs

Quando trabalhamos com o Navigation pode acontecer de acharmos um arquivo de Navigation Graph muito grande. Para resolver esse problema o Navigation oferece uma técnica chamada Nested Graph, que funciona como um agrupamento de vários destinations. Em um caso real, podemos pensar que cada nested graph poderia ser um fluxo diferente da nossa aplicação.

Podemos criar um nested graph de duas formas. A primeira delas é criar outro componente de navigation dentro do navigation graph principal da nossa aplicação, por exemplo.

No Design o Nested Graph é apresentado da seguinte forma:

Outra forma de criarmos um Nested Graph é criar um novo arquivo de Navigation Graph e adicioná-lo por meio do include no graph principal. Para referenciar um graph no include vamos usar uma tag chamada app:graph

E é isso!

Essa foi uma pequena apresentação sobre algumas funcionalidades do Navigation, que resolve alguns problemas ou dificuldades que temos quando trabalhamos com fragments e navegações complexas no Android. No momento, o Navigation está em beta, mas vamos esperar por novidades e ver como vai ser a evolução deste componente que, até o momento, estou achando muito bom. Ficou alguma dúvida ou tem alguma observação adicional ao tema? Aproveite os campos abaixo. Até a próxima!

Quer saber mais? Aqui está o link do projeto que usei para o post e aqui está a library do Navigation.