💻 Exemplos: Capítulo 09: Navegação com Navigation Compose
Exemplos de Código: Capítulo 09 — Navegação com Navigation Compose
Especialização em Mobile Nativo com Android • Demonstrações Práticas Compiláveis
💻 Código de Demonstração: Type-Safe Navigation Compose e Passagem Segura de Parâmetros
O código abaixo demonstra a navegação moderna no Jetpack Compose com suporte à verificação de tipos em tempo de compilação (Type-Safe Navigation):
- Definição de rotas imutáveis com
@Serializable(Navigation 2.8+). - Orquestração do grafo de telas com
NavHosterememberNavController. - Passagem de argumentos complexos (ID e Nome) sem concatenação insegura de strings de URL.
- Navegação reversa via pilha (Back Stack).
package br.com.portal.mobile.android.cap09
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import kotlinx.serialization.Serializable
// =====================================================================
// 1. Definição de Destinos Fortemente Tipados (Type-Safe Routes)
// =====================================================================
@Serializable
object HomeRoute
@Serializable
data class DetalhesProdutoRoute(
val produtoId: String,
val nomeProduto: String,
val preco: Double
)
// =====================================================================
// 2. Grafo de Navegação (NavHost)
// =====================================================================
@Composable
fun AppNavigationHost() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = HomeRoute
) {
// Tela 1: Catálogo de Produtos
composable<HomeRoute> {
HomeScreen(
onNavegarParaDetalhes = { id, nome, preco ->
// Navega passando a instância tipada da rota
navController.navigate(
DetalhesProdutoRoute(
produtoId = id,
nomeProduto = nome,
preco = preco
)
)
}
)
}
// Tela 2: Detalhes do Produto Selecionado
composable<DetalhesProdutoRoute> { backStackEntry ->
// Extração segura dos argumentos sem cast manual
val rotaArgs = backStackEntry.toRoute<DetalhesProdutoRoute>()
DetalhesProdutoScreen(
args = rotaArgs,
onVoltar = { navController.popBackStack() }
)
}
}
}
// =====================================================================
// 3. Telas da Aplicação
// =====================================================================
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(onNavegarParaDetalhes: (String, String, Double) -> Unit) {
Scaffold(
topBar = { TopAppBar(title = { Text("Catálogo de Produtos") }) }
) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Selecione um Produto",
style = MaterialTheme.typography.headlineMedium
)
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = { onNavegarParaDetalhes("PRD-101", "Smartphone Pixel 8 Pro", 4999.00) },
modifier = Modifier.fillMaxWidth()
) {
Text("Ver Pixel 8 Pro (R$ 4.999,00)")
}
Spacer(modifier = Modifier.height(12.dp))
Button(
onClick = { onNavegarParaDetalhes("PRD-202", "Fone Pixel Buds Pro", 1299.00) },
modifier = Modifier.fillMaxWidth()
) {
Text("Ver Pixel Buds Pro (R$ 1.299,00)")
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DetalhesProdutoScreen(
args: DetalhesProdutoRoute,
onVoltar: () -> Unit
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Detalhes do Produto") },
navigationIcon = {
IconButton(onClick = onVoltar) {
Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Voltar")
}
}
)
}
) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp)
) {
Text(text = "Código: ${args.produtoId}", style = MaterialTheme.typography.labelMedium)
Spacer(modifier = Modifier.height(8.dp))
Text(text = args.nomeProduto, style = MaterialTheme.typography.headlineLarge)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "R$ ${String.format("%.2f", args.preco)}",
style = MaterialTheme.typography.displaySmall,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(32.dp))
OutlinedButton(onClick = onVoltar, modifier = Modifier.fillMaxWidth()) {
Text("Voltar ao Catálogo")
}
}
}
}
@Preview(showBackground = true)
@Composable
fun DetalhesPreview() {
MaterialTheme {
DetalhesProdutoScreen(
args = DetalhesProdutoRoute("123", "Pixel 8 Pro", 4999.0),
onVoltar = {}
)
}
}