Neste artigo, mostrarei como utilizar o Navigation, em um projeto modularizado.
O Navigation é um componente que faz parte do Jetpack.
O Navigation consiste de três partes principais:
- NavGraph: é um XML que centraliza as informações de navegação de uma feature.
- NavHost: é um contêiner vazio que mostra destinos do gráfico de navegação. O componente de navegação contém uma implementação NavHostpadrão, NavHostFragment, que mostra os destinos do fragmento.
- NavController: é um objeto que gerencia a navegação do aplicativo em um NavHost. O NavController organiza a troca do conteúdo de destino no NavHost conforme os usuários se movem pelo aplicativo.
Texto completo aqui .
Porque utilizar o Navigation ?
Com o Navigation é possível ver todo a navegação através do NavGraph , não sendo necessário assim procurar por cada Intent e FragmentTransaction no projeto e para encontrar todos os destinos possíveis.
Não é mais necessário se preocupar com o fato de o botão de voltar sair do aplicativo ao vir de um deep link e falar de deep link, eles também têm essa informação da navegação.
Segurança ao passar argumentos utilizando o SafeArgs . Não é mais necessário fazer verificações e proteções de segurança, porque é possível ter os argumentos com tipos definidos.
Ele também funciona com os padrões atuais da interface do usuário de navegação, como [bottom nav] (https://material.io/components/bottom-navigation), com apenas algumas configurações e pode lidar com transições e animações 💙 entre a navegação.
Pré requisitos
Para utilizar o Navigation é necessário ter instalado o Android Studio na versão 3.3 ou superior.
E adicionar no build.gradle do projeto as dependências do navigation:
implementation "androidx.navigation:navigation-fragment-ktx:2.2.2" | |
implementation "androidx.navigation:navigation-ui-ktx:2.2.2" |
[^]: No momento da criação deste artigo a versão mais recente é a 2.2.2
Em projetos monolitos temos todas as features e camadas em um único módulo, mas nem todas features precisam uma das outras, isso também se aplica a navegação.
Hands On
Como exemplo temos uma feature 1 que tem 2 fragments e faz a navegação de Fragment A -> Fragment B
Utilizando o navigation, precisaremos apenas de um NavGraphcom nossos 2 destinos, e um action que leva do Fragment A ao Fragment B
<?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_feature_one" | |
app:startDestination="@id/fragment_a"> | |
<fragment | |
android:id="@+id/fragment_a" | |
android:name="io.jgabriel.featureone.FragmentA"> | |
<action | |
android:id="@+id/action_go_to_b" | |
app:destination="@+id/fragment_b" | |
app:popUpTo="@+id/fragment_a" /> | |
</fragment> | |
<fragment | |
android:id="@+id/fragment_b" | |
android:name="io.jgabriel.featureone.FragmentB"/> | |
</navigation> |
Assim temos a navegação da nossa primeira feature.
Agora iremos aumentar a nossa aplição incluindo mais 2 feature modules :
Nossa aplicação agora contém essas features e fragments :
Será necessário também adicionar os NavGraphspara as novas features criadas seguindo o mesmo modelo da Feature 1
<?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_feature_two" | |
app:startDestination="@id/fragment_c"> | |
<fragment | |
android:id="@+id/fragment_c" | |
android:name="io.jgabriel.featuretwo.FragmentC"> | |
<action | |
android:id="@+id/action_go_to_d" | |
app:destination="@+id/fragment_d" | |
app:popUpTo="@+id/fragment_c" /> | |
</fragment> | |
<fragment | |
android:id="@+id/fragment_d" | |
android:name="io.jgabriel.featuretwo.FragmentD"/> | |
</navigation> |
<?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_feature_three" | |
app:startDestination="@id/fragment_e"> | |
<fragment | |
android:id="@+id/fragment_e" | |
android:name="io.jgabriel.featurethree.FragmentE"> | |
<action | |
android:id="@+id/action_go_to_f" | |
app:destination="@+id/fragment_f" | |
app:popUpTo="@+id/fragment_e" /> | |
</fragment> | |
<fragment | |
android:id="@+id/fragment_f" | |
android:name="io.jgabriel.featurethree.FragmentF" /> | |
</navigation> |
os adicionar as nossas feature ao módulo de aplicação através do build.gradle
implementation project(":featureone") | |
implementation project(":featuretwo") | |
implementation project(":featurethree") |
Agora o nosso módulo de aplicação conhece as nossas 3 features.
Para não precisar repetir toda a navegação feita em cada feature, vamos reutilizar os NavGraphs, através da tag de include dentro do NavGraph do nosso módulo e aplicação
<?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_main" | |
app:startDestination="@id/nav_feature_one"> | |
<include app:graph="@navigation/nav_feature_one" /> | |
<include app:graph="@navigation/nav_feature_two" /> | |
<include app:graph="@navigation/nav_feature_three" /> | |
</navigation> |
Agora já é possível fazer a navegação de qualquer fragment dentro do módulo de aplicação.
Em problemas reais, geralmente precisamos acessar um fragment de uma feature, através de outra por exemplo:
O Fragment B que está na Feature 1, precisa navegar até Fragment D que está na Feature 2 :
Para isso vamos adicionar deep links aos fragments e fazer a navegação através deles, assim as features continuarão independentes e o módulo de app será o responsável por gerenciar essa navegação.
NavGraph Feature Two
<?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_feature_two" | |
app:startDestination="@id/fragment_c" | |
tools:ignore="UnusedNavigation"> | |
<fragment | |
android:id="@+id/fragment_c" | |
android:name="io.jgabriel.featuretwo.FragmentC"> | |
<action | |
android:id="@+id/action_go_to_d" | |
app:destination="@+id/fragment_d" | |
app:popUpTo="@+id/fragment_c" /> | |
</fragment> | |
<fragment | |
android:id="@+id/fragment_d" | |
android:name="io.jgabriel.featuretwo.FragmentD"> | |
<deepLink app:uri="myApp://fragmentD" /> | |
</fragment> | |
</navigation> |
Utilizando a tag deepLink, podemos especificar o URI que podemos usar para navegar para esse destino específico, sem a
Feature One precisar conhecer a nossa Feature Two.
Para fazer essa navegação vamos adicionar um click no FragmentB que vai navegar até o FragmentD
class FragmentB : Fragment() { | |
fun onViewCreated(…) { | |
... | |
... | |
view.setOnClickListener { | |
val uri = Uri.parse("myApp://fragmentD") | |
findNavController().navigate(uri) | |
} | |
} | |
} |
E com isso mantemos os nossos módulos independentes um dos outros mas capazes de navegarem entre si.
Obrigado por ler até aqui todo o código utilizado está disponível no meu Git .
Outros artigos que podem ser interessantes para você:
- Kotlin Koroutines
- Android Java interfaces like a boss!
- Kotlin: Crie sua própria DSL utilizando expressões Lambda
- Arquiteturas avançadas Android VIPER