Projetar o gráfico de navegação

O componente Navigation usa um gráfico de navegação para gerenciar a navegação do seu app. O gráfico de navegação é uma estrutura de dados que contém cada destino no app e as conexões entre eles.

Tipos de destino

Existem três tipos gerais de destinos: hospedado, caixa de diálogo e atividade. A tabela abaixo descreve esses três tipos de destino e as finalidades deles.

Tipo

Descrição

Casos de uso

Hospedado

Preenche todo o host de navegação. Ou seja, o tamanho de um destino hospedado é o mesmo do host de navegação, e os destinos anteriores não são visíveis.

Telas principal e de detalhes.

Caixa de diálogo

Apresenta componentes de interface de sobreposição. Essa interface não está vinculada ao local nem ao tamanho do host de navegação. Os destinos anteriores ficam visíveis abaixo dele.

Alertas, seleções, formulários.

Atividade

Representa telas ou recursos únicos no app.

Serve como um ponto de saída para o gráfico de navegação que inicia uma nova atividade do Android gerenciada separadamente do componente Navigation.

No Modern Android Development, um app consiste em uma única atividade. Os destinos de atividade são mais indicados para interagir com atividades de terceiros ou como parte do processo de migração.

Este documento contém exemplos de destinos hospedados, que são os destinos mais comuns e fundamentais. Consulte os guias abaixo para mais informações sobre outros destinos:

Estruturas de trabalho

Embora o mesmo fluxo de trabalho geral se aplique a todos os casos, a maneira exata de criar um host e um gráfico de navegação depende do framework da interface que você usa.

  • Compose: use o elemento combinável NavHost. Adicione um NavGraph a ele usando a DSL do Kotlin (link em inglês). É possível criar o gráfico de duas maneiras:
    • Como parte do NavHost: construa o gráfico de navegação diretamente como parte da adição do NavHost.
    • De forma programática: use o método NavController.createGraph() para criar um NavGraph e transmiti-lo diretamente para o NavHost.
  • Fragmentos:ao usar fragmentos com o framework de interface de visualizações, use um NavHostFragment como host. Há várias maneiras de criar um gráfico de navegação:
    • De forma programática:use a DSL do Kotlin para criar uma NavGraph e aplicá-la diretamente à NavHostFragment.
      • A função createGraph() usada com a DSL do Kotlin para fragmentos e no Compose é a mesma.
    • XML: programe o host e o gráfico de navegação diretamente em XML.
    • Editor do Android Studio: use o editor de GUI no Android Studio para criar e ajustar o gráfico como um arquivo de recurso XML.

Escrever

No Compose, use um objeto ou uma classe serializável para definir uma rota. Uma rota descreve como chegar a um destino e contém todas as informações necessárias a ele. Depois de definir suas rotas, use o elemento combinável NavHost para criar o gráfico de navegação. Confira o exemplo abaixo:

@Serializable
object Profile
@Serializable
object FriendsList

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
    // Add more destinations similarly.
}
  1. Um objeto serializável representa cada uma das duas rotas, Profile e FriendsList.
  2. A chamada para o elemento combinável NavHost transmite um NavController e uma rota para o destino inicial.
  3. A lambda transmitida ao NavHost chama NavController.createGraph() e retorna um NavGraph.
  4. Cada rota é fornecida como um argumento de tipo para NavGraphBuilder.composable<T>(), que adiciona o destino ao NavGraph resultante.
  5. O lambda transmitido para composable é o que o NavHost exibe para esse destino.

Entender o lambda

Para entender melhor a lambda que cria o NavGraph, considere que, para criar o mesmo gráfico do snippet anterior, você poderia criar o NavGraph separadamente usando NavController.createGraph() e transmitindo diretamente para o NavHost:

val navGraph by remember(navController) {
  navController.createGraph(startDestination = Profile)) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
  }
}
NavHost(navController, navGraph)

Argumentos de transmissão

Se você precisar transmitir dados para um destino, defina a rota com uma classe que tenha parâmetros. Por exemplo, a rota Profile é uma classe de dados com um parâmetro name.

@Serializable
data class Profile(val name: String)

Sempre que você precisar transmitir argumentos para esse destino, crie uma instância da classe de rota, transmitindo os argumentos para o construtor da classe.

Receber instância de rota

Você pode acessar a instância de rota com NavBackStackEntry.toRoute() ou SavedStateHandle.toRoute(). Quando você cria um destino usando composable(), o NavBackStackEntry fica disponível como um parâmetro.

@Serializable
data class Profile(val name: String)

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile(name="John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(name = profile.name) }
}

Observe o seguinte neste snippet:

  • A rota Profile especifica o destino inicial no gráfico de navegação, com "John Smith" como argumento para name.
  • O destino em si é o bloco composable<Profile>{}.
  • O elemento combinável ProfileScreen usa o valor de profile.name para o próprio argumento name.
  • Assim, o valor "John Smith" é transmitido para ProfileScreen.

Exemplo mínimo

Um exemplo completo de NavController e NavHost trabalhando juntos:

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// Define the ProfileScreen composable.
@Composable
fun ProfileScreen(
    profile: Profile
    onNavigateToFriendsList: () -> Unit,
  ) {
  Text("Profile for ${profile.name}")
  Button(onClick = { onNavigateToFriendsList() }) {
    Text("Go to Friends List")
  }
}

// Define the FriendsListScreen composable.
@Composable
fun FriendsListScreen(onNavigateToProfile: () -> Unit) {
  Text("Friends List")
  Button(onClick = { onNavigateToProfile() }) {
    Text("Go to Profile")
  }
}

// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp() {
  val navController = rememberNavController()
  NavHost(navController, startDestination = Profile(name = "John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(
            profile = profile,
            onNavigateToFriendsList = {
                navController.navigate(route = FriendsList)
            }
        )
    }
    composable<FriendsList> {
      FriendsListScreen(
        onNavigateToProfile = {
          navController.navigate(
            route = Profile(name = "Aisha Devi")
          )
        }
      )
    }
  }
}

Como demonstrado no snippet, em vez de transmitir o NavController para os elementos combináveis, exponha um evento para o NavHost. Ou seja, os elementos combináveis precisam ter um parâmetro do tipo () -> Unit para o qual o NavHost transmite uma lambda que chama NavController.navigate().

Fragmentos

Conforme descrito nas seções anteriores, ao usar fragmentos, você tem a opção de criar um gráfico de navegação de forma programática usando a DSL do Kotlin, o XML ou o editor do Android Studio.

As seções abaixo detalham essas diferentes abordagens.

De forma programática

A DSL do Kotlin oferece uma maneira programática de criar um gráfico de navegação com fragmentos. Isso acaba ficando mais bem organizado e moderno do que usar um arquivo de recurso XML.

Confira o exemplo abaixo, que implementa um gráfico de navegação em duas telas.

Primeiro, é necessário criar o NavHostFragment, que não pode incluir um elemento app:navGraph:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

Em seguida, transmita o id do NavHostFragment para NavController.findNavController. Isso associa o NavController ao NavHostFragment.

Em seguida, a chamada para NavController.createGraph() vincula o gráfico ao NavController e, consequentemente, também ao NavHostFragment:

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// Retrieve the NavController.
val navController = findNavController(R.id.nav_host_fragment)

// Add the graph to the NavController with `createGraph()`.
navController.graph = navController.createGraph(
    startDestination = Profile(name = "John Smith")
) {
    // Associate each destination with one of the route constants.
    fragment<ProfileFragment, Profile> {
        label = "Profile"
    }

    fragment<FriendsListFragment, FriendsList>() {
        label = "Friends List"
    }

    // Add other fragment destinations similarly.
}

O uso da DSL dessa maneira é muito semelhante ao fluxo de trabalho descrito na seção anterior no Compose. Por exemplo, lá e aqui, a função NavController.createGraph() gera o NavGraph. Da mesma forma, enquanto NavGraphBuilder.composable() adiciona destinos combináveis ao gráfico, aqui NavGraphBuilder.fragment() adiciona um destino de fragmento.

Para saber mais sobre como usar a DSL do Kotlin, consulte Criar um gráfico com a DSL do NavGraphBuilder.

XML

Você pode criar o XML diretamente. O exemplo abaixo espelha e é equivalente ao exemplo de duas telas da seção anterior.

Primeiro, crie um NavHostFragment. Ele serve como o host de navegação, que contém o gráfico de navegação real.

Uma implementação mínima de um NavHostFragment:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/nav_graph" />

</FrameLayout>

O NavHostFragment contém o atributo app:navGraph. Use esse atributo para conectar seu gráfico de navegação ao host de navegação. Confira abaixo um exemplo de como implementar o gráfico:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/profile">

    <fragment
        android:id="@+id/profile"
        android:name="com.example.ProfileFragment"
        android:label="Profile">

        <!-- Action to navigate from Profile to Friends List. -->
        <action
            android:id="@+id/action_profile_to_friendslist"
            app:destination="@id/friendslist" />
    </fragment>

    <fragment
        android:id="@+id/friendslist"
        android:name="com.example.FriendsListFragment"
        android:label="Friends List" />

    <!-- Add other fragment destinations similarly. -->
</navigation>

Você usa ações para definir as conexões entre destinos diferentes. Neste exemplo, o fragmento profile contém uma ação que navega para friendslist. Para saber mais, consulte Usar ações e fragmentos de navegação.

Editor

Você pode gerenciar o gráfico de navegação do seu app usando o Navigation Editor no Android Studio. Essencialmente, essa é uma GUI que pode ser usada para criar e editar o XML do NavigationFragment, conforme mostrado na seção anterior.

Para mais informações, consulte o Navigation Editor.

Gráficos aninhados

Também é possível usar gráficos aninhados. Isso envolve o uso de um gráfico como destino de navegação. Para mais informações, consulte Gráficos aninhados.

Leia mais

Para conferir mais dos principais conceitos de navegação, consulte os guias abaixo: