Jetpack Compose: Navigation
Tags: android, jetpack, navigation, kotlin
Published: 2021-08-19 12:12:01

A navegação dentro de um app Android pode ser feito de várias formas. Contudo o Jetpack apresenta um facilitador que é o _Navigation Component_.

O Navigation Component consiste em 3 partes:
1. Navjgation graph: um recurso XML que mostra graficamente todos os caminhos e destinos da sua navegação pretendida;
2. `NavHost`: um container que irá exibir os destinos dos gráficos;
3. `NavController`: um objeto que gerencia a navegação dentro de um `NavHost` e orquestra a troca de destinos e interações.

Para poder começar a usar o _Navigation Component_ primeiro é necessário adicionar as seguintes dependências no build.gradle:

```gradle
dependencies {
  def nav_version = "2.3.5"

  // Java language implementation
  implementation "androidx.navigation:navigation-fragment:$nav_version"
  implementation "androidx.navigation:navigation-ui:$nav_version"

  // Kotlin
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

  // Feature module Support
  implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"

  // Testing Navigation
  androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"

  // Jetpack Compose Integration
  implementation "androidx.navigation:navigation-compose:2.4.0-alpha06"
}
```

Após adicionar as referências já será possível adicionar um _Navigation Graph_ no projeto, da seguinte forma:
* Clique bom o botão direito em _res_;
* Vá em **New** > **Android Resource File**;
* Escolha um nome para o arquivo
* Em _Resource Type_ escolha **Navigation**

Será criado um _Navigation Graph_ vazio onde será possível adicionar os destinos e as ações necessárias. O XML deste arquivo deve se parece com algo do tipo:

```xml
<?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"
            android:id="@+id/nav_graph">

</navigation>
```

##Incluindo um NavHost
No passo seguinte precisamos incluir um NavHost em uma Activity para realizar a orquestração da navegação. Para tanto abra o XML da Activity principal e incluir o _NavHost_:

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        .../>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        .../>

</androidx.constraintlayout.widget.ConstraintLayout>
```
Importante:
* `android:name` contém o nome da classe da implementação do seu `NavHost` (chegaremos lá);
* `app:navGraph` é o nome do arquivo navigation criado no passo anterior;
* `app:defaultNavHost` quando definido como `true` indica que esse NavHost é o padrão e ele responde ao botão _Back_.

##Adicionando Destinos
Você pode adicionar novos destinos ao _Navigation Graph_ apontando para Fragments existentes ou criando novos. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udyzypt6i23a98gowkvm.png)

Em um destino temos 4 atributos principais:
* **id** define o id do destino e como deve ser referenciado no código;
* **name** é caminho da classe que responde pelo código deste destino;
* **label** nome legível do destino;
* **layout** arquivo xml usado para renderizar o destino

No exemplo acima teremos um XML parecido com este:

```xml
<?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/fragmentMain">

    <fragment
        android:id="@+id/fragmentMain"
        android:name="com.example.navcomp.FragmentMain"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >
        <action
            android:id="@+id/action_fragmentMain_to_fragmentSecond"
            app:destination="@id/fragmentSecond" />
    </fragment>
    <fragment
        android:id="@+id/fragmentThird"
        android:name="com.example.navcomp.FragmentThird"
        android:label="fragment_third"
        tools:layout="@layout/fragment_third" />
    <fragment
        android:id="@+id/fragmentSecond"
        android:name="com.example.navcomp.FragmentSecond"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" >
        <action
            android:id="@+id/action_fragmentSecond_to_fragmentThird"
            app:destination="@id/fragmentThird" />
    </fragment>
</navigation>
```
Um item que você deve ter notado no XML acima que não foi falado são as actions. Uma action representa uma conexão lógica entre destinos. As actions auxiliam na hora da escrita do código dizendo como e para qual destino uma action deve trafegar. 
Para criar um action basta arrastar o mouse de um destino para o outro. Uma action tem os seguintes atributos notáveis:

* **id** contendo o id da action para ser referenciado no código;
* **destination** o id do destino para onde vai quando a action for executada.

A navegação é executada de fato usando um `NavController`, que é um objeto que fica dentro de um `NavHost`. Existem 3 formas de acessar um `NavController`:
* `Fragment.findNavController()`
* `View.findNavController()`
* `Activity.findNavController(viewId: Int)`

```kotlin
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
```

Agora o exemplo completo da navegação:

**activity_main.xml**
```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Go To 2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Go To 3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent" />



    <androidx.fragment.app.FragmentContainerView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
```

e o MainActivity.kt
```kotlin
package com.example.navcomp

import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import androidx.navigation.NavController
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
        val navController = navHostFragment?.findNavController();

        var button1 = findViewById<Button>(R.id.button1);
        var button2 = findViewById<Button>(R.id.button2);

        button1.setOnClickListener {
            navController?.navigate(R.id.action_fragmentMain_to_fragmentSecond);
        }

        button2.setOnClickListener {
            navController?.navigate(R.id.action_fragmentSecond_to_fragmentThird);
        }
    }
}
```

Esse exemplo foi bem básico. Há muito mais o que explorar como parâmetros tipados com o SafeArgs, Deep Links, Nested Graphs e outros.

[Código Completo](https://github.com/dgbarreto/basicnavigationcomponent)