Jetpack Compose: Navigation

dgbarreto

Danilo Barreto

Posted on August 19, 2021

Jetpack Compose: Navigation

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:

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"
}
Enter fullscreen mode Exit fullscreen mode

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 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>
Enter fullscreen mode Exit fullscreen mode

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 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>
Enter fullscreen mode Exit fullscreen mode

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

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 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>
Enter fullscreen mode Exit fullscreen mode

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)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
Enter fullscreen mode Exit fullscreen mode

Agora o exemplo completo da navegação:

activity_main.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>
Enter fullscreen mode Exit fullscreen mode

e o MainActivity.kt

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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

💖 💪 🙅 🚩
dgbarreto
Danilo Barreto

Posted on August 19, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related