Flutter Navigation 2.0 e GoRouter em 2026: Deep Linking e Perguntas de Entrevista
Dominar a navegação Flutter com GoRouter 17.5: roteamento declarativo, deep linking, ShellRoute, guards de rota e perguntas de entrevista com exemplos práticos.

A navegação Flutter evoluiu significativamente desde a introdução do Navigation 2.0. O GoRouter 17.5, o pacote oficial da equipe Flutter, agora lida com a complexidade da API Router enquanto fornece roteamento declarativo, deep linking automático e navegação aninhada prontos para uso.
A equipe Flutter considera o GoRouter funcionalmente completo desde 2026. Ele suporta parâmetros de caminho, parâmetros de consulta, redirecionamentos, ShellRoute para UI persistente e StatefulShellRoute para preservar o estado das abas. O Navigator 2.0 bruto raramente é escrito manualmente em produção.
Compreendendo a Arquitetura do Navigator 2.0
O Navigator 2.0 introduziu uma abordagem declarativa baseada em URLs para navegação Flutter. Em vez de chamadas imperativas push e pop, o estado da aplicação determina o que aparece na pilha de navegação. A arquitetura se baseia em três classes fundamentais:
- Router: O widget de nível superior que coordena a navegação
- RouteInformationParser: Traduz URLs em estado de aplicação
- RouterDelegate: Constrói a árvore de widgets com base nesse estado
Quando um deep link chega do sistema operacional ou uma URL muda em um navegador web, o framework atualiza o estado e a UI reage. O problema: escrever um RouterDelegate e RouteInformationParser personalizados manualmente requer código boilerplate considerável.
// Raw Navigator 2.0 approach - verbose and rarely used directly
class AppRouterDelegate extends RouterDelegate<AppRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppRoutePath> {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
AppRoutePath? _currentPath;
AppRoutePath? get currentConfiguration => _currentPath;
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
const MaterialPage(child: HomeScreen()),
if (_currentPath?.isProductPage == true)
MaterialPage(child: ProductScreen(id: _currentPath!.productId!)),
],
onDidRemovePage: (page) {
// Handle page removal
},
);
}
Future<void> setNewRoutePath(AppRoutePath path) async {
_currentPath = path;
notifyListeners();
}
}Essa verbosidade é o motivo da existência do GoRouter. Ele encapsula o Navigator 2.0 e lida com o código boilerplate.
Configuração do GoRouter 17.5
O GoRouter 17.5 introduz suporte para metadados de rota e restrições com expressões regulares para parâmetros de caminho. O SDK mínimo requerido é Flutter 3.32 e Dart 3.8.
import 'package:go_router/go_router.dart';
final goRouter = GoRouter(
initialLocation: '/',
debugLogDiagnostics: true, // Useful during development
routes: [
GoRoute(
path: '/',
name: 'home',
builder: (context, state) => const HomeScreen(),
routes: [
GoRoute(
path: 'products/:id', // Path parameter
name: 'product',
builder: (context, state) {
final productId = state.pathParameters['id']!;
return ProductScreen(productId: productId);
},
),
GoRoute(
path: 'search',
name: 'search',
builder: (context, state) {
// Query parameters: /search?q=flutter&category=books
final query = state.uri.queryParameters['q'] ?? '';
final category = state.uri.queryParameters['category'];
return SearchScreen(query: query, category: category);
},
),
],
),
],
);O router se integra com MaterialApp.router:
import 'package:flutter/material.dart';
import 'router_config.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: goRouter,
title: 'GoRouter Demo',
);
}
}Configuração de Deep Linking para Android e iOS
O deep linking permite que URLs externas abram telas específicas no aplicativo. O GoRouter lida com o roteamento automaticamente uma vez que a configuração da plataforma está pronta.
Android App Links
O Android requer um intent-filter no AndroidManifest.xml e um arquivo Digital Asset Links hospedado no domínio:
<!-- android/app/src/main/AndroidManifest.xml -->
<activity android:name=".MainActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="example.com"
android:pathPrefix="/products" />
</intent-filter>
</activity>O arquivo assetlinks.json deve ser servido em https://example.com/.well-known/assetlinks.json.
iOS Universal Links
Para iOS, a capacidade Associated Domains deve ser habilitada no Xcode, e um arquivo apple-app-site-association deve ser hospedado:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAMID.com.example.app",
"paths": ["/products/*", "/search"]
}
]
}
}O GoRouter 17.5 corrigiu um bug crítico onde deep links do Android em inicialização a frio com caminhos vazios perdiam o esquema e a autoridade. Isso significa que os deep links agora funcionam de forma confiável mesmo quando o aplicativo não está em execução.
Para testar deep links no Android usar adb shell am start -a android.intent.action.VIEW -d "https://example.com/products/123". Para iOS, usar xcrun simctl openurl booted "https://example.com/products/123".
ShellRoute para UI de Navegação Persistente
ShellRoute envolve as rotas filhas com um elemento de UI persistente como uma BottomNavigationBar ou um Drawer. O shell permanece visível enquanto navega entre seus filhos.
final goRouter = GoRouter(
initialLocation: '/home',
routes: [
ShellRoute(
builder: (context, state, child) {
return ScaffoldWithNavBar(child: child);
},
routes: [
GoRoute(
path: '/home',
name: 'home',
builder: (context, state) => const HomeTab(),
),
GoRoute(
path: '/explore',
name: 'explore',
builder: (context, state) => const ExploreTab(),
),
GoRoute(
path: '/profile',
name: 'profile',
builder: (context, state) => const ProfileTab(),
),
],
),
],
);
// scaffold_with_nav_bar.dart
class ScaffoldWithNavBar extends StatelessWidget {
final Widget child;
const ScaffoldWithNavBar({super.key, required this.child});
Widget build(BuildContext context) {
return Scaffold(
body: child,
bottomNavigationBar: BottomNavigationBar(
currentIndex: _calculateSelectedIndex(context),
onTap: (index) => _onItemTapped(index, context),
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.explore), label: 'Explore'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
);
}
int _calculateSelectedIndex(BuildContext context) {
final location = GoRouterState.of(context).uri.toString();
if (location.startsWith('/explore')) return 1;
if (location.startsWith('/profile')) return 2;
return 0;
}
void _onItemTapped(int index, BuildContext context) {
switch (index) {
case 0: context.go('/home');
case 1: context.go('/explore');
case 2: context.go('/profile');
}
}
}O GoRouter 17.5 corrigiu um problema onde os gestos de voltar no iOS removiam todo o ShellRoute em vez de apenas a sub-rota ativa.
Pronto para mandar bem nas entrevistas de Flutter?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
StatefulShellRoute para Preservar Estado das Abas
Quando os usuários trocam de aba, ShellRoute reconstrói o filho. StatefulShellRoute preserva a pilha de navegação de cada branch:
final goRouter = GoRouter(
initialLocation: '/home',
routes: [
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) {
return ScaffoldWithNavBar(navigationShell: navigationShell);
},
branches: [
StatefulShellBranch(
routes: [
GoRoute(
path: '/home',
builder: (context, state) => const HomeTab(),
routes: [
GoRoute(
path: 'details/:id',
builder: (context, state) => DetailsScreen(
id: state.pathParameters['id']!,
),
),
],
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/explore',
builder: (context, state) => const ExploreTab(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileTab(),
),
],
),
],
),
],
);Com essa configuração, se um usuário navega para /home/details/42, muda para a aba Explorar, depois retorna para Início, ele ainda estará na tela de detalhes.
Guards de Rota com Redirect
Os redirecionamentos lidam com verificações de autenticação e navegação condicional. O callback redirect é executado antes de cada navegação:
final goRouter = GoRouter(
initialLocation: '/',
redirect: (context, state) {
final isLoggedIn = AuthService.instance.isLoggedIn;
final isLoggingIn = state.matchedLocation == '/login';
// Redirect to login if not authenticated
if (!isLoggedIn && !isLoggingIn) {
return '/login?redirect=${state.uri}';
}
// Redirect away from login if already authenticated
if (isLoggedIn && isLoggingIn) {
final redirect = state.uri.queryParameters['redirect'];
return redirect ?? '/';
}
return null; // No redirect
},
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
),
GoRoute(
path: '/login',
builder: (context, state) => const LoginScreen(),
),
GoRoute(
path: '/admin',
redirect: (context, state) {
// Route-level redirect for admin-only access
if (!AuthService.instance.isAdmin) {
return '/';
}
return null;
},
builder: (context, state) => const AdminScreen(),
),
],
);O GoRouter 17.5 adicionou suporte para usar métodos de extensão de contexto como context.namedLocation() dentro dos callbacks de redirecionamento.
Perguntas de Entrevista Comuns sobre Navegação Flutter
Entrevistadores frequentemente avaliam o conhecimento de navegação porque toca arquitetura, gerenciamento de estado e integração de plataforma. Aqui estão perguntas que distinguem candidatos seniores:
P: Qual problema o Navigator 2.0 resolve que o Navigator 1.0 não resolve?
O Navigator 1.0 usa navegação imperativa: Navigator.push() e Navigator.pop(). A aplicação não pode representar o estado de navegação como uma URL, tornando difícil o deep linking e o suporte web. O Navigator 2.0 é declarativo: o estado da aplicação determina a pilha de navegação, URLs são cidadãos de primeira classe, e deep links funcionam automaticamente.
P: Quando usar context.go() vs context.push()?
context.go('/path') substitui a pilha de navegação atual até a rota correspondente. context.push('/path') adiciona uma nova rota no topo da pilha existente. Usar go() para navegação de nível superior (trocar abas, ir para início) e push() para aprofundar em detalhes enquanto preserva a navegação de volta.
P: Como preservar o estado entre trocas de abas?
Usar StatefulShellRoute.indexedStack(). Ele mantém pilhas de navegação separadas para cada branch e as preserva ao trocar de aba. Sem isso, trocar de aba reconstrói toda a sub-árvore.
P: Como passar objetos complexos entre rotas?
Existem três abordagens. Primeiro, passar um ID no parâmetro de caminho e buscar o objeto na tela de destino. Segundo, usar o parâmetro extra do GoRouter para passar o objeto diretamente. Terceiro, armazenar o objeto em uma solução de gerenciamento de estado (Riverpod, Bloc) e acessá-lo do destino. A primeira abordagem é melhor para deep linking porque a URL permanece compartilhável.
// Passing via extra (not deep-linkable)
context.push('/product', extra: productObject);
// Receiving via extra
GoRoute(
path: '/product',
builder: (context, state) {
final product = state.extra as Product?;
return ProductScreen(product: product);
},
),P: O que é o callback onEnter no GoRouter 17.5?
O callback onEnter é executado ao entrar em uma rota e fornece acesso tanto ao estado de rota atual quanto ao próximo. Ele permite executar lógica antes que a rota seja completamente carregada, útil para analytics ou pré-carregamento condicional.
Rotas Type-Safe com Geração de Código
O GoRouter suporta roteamento type-safe através de geração de código. Basta definir classes de dados de rota e deixar o build_runner gerar o código de navegação:
import 'package:go_router/go_router.dart';
part 'routes.g.dart';
<HomeRoute>(path: '/')
class HomeRoute extends GoRouteData {
const HomeRoute();
Widget build(BuildContext context, GoRouterState state) => const HomeScreen();
}
<ProductRoute>(path: '/product/:id')
class ProductRoute extends GoRouteData {
final String id;
const ProductRoute({required this.id});
Widget build(BuildContext context, GoRouterState state) =>
ProductScreen(productId: id);
}Após executar dart run build_runner build, a navegação tem segurança em tempo de compilação:
// Type-safe navigation
ProductRoute(id: '123').go(context);Isso elimina erros de digitação nos caminhos de rota e garante que os parâmetros sejam sempre fornecidos.
Dominando a Navegação Flutter para Aplicações de Produção
- GoRouter 17.5 é a solução de navegação recomendada para aplicações Flutter que requerem deep linking, navegação aninhada ou suporte web
- Usar
ShellRoutepara barras de navegação inferiores persistentes; usarStatefulShellRoutepara preservar a pilha de navegação de cada aba - Configurar o deep linking específico de plataforma (Android App Links, iOS Universal Links) separadamente do GoRouter, que lida com a lógica de roteamento
- Implementar guards de autenticação com o callback
redirectno nível do router ou no nível de rota individual - Para cenários de entrevista, focar em explicar o modelo declarativo: o estado da aplicação direciona a UI, URLs são derivadas do estado, e o GoRouter lida com a tradução
- Rotas type-safe com geração de código eliminam erros de tempo de execução por caminhos mal digitados
- Consultar a documentação de navegação Flutter e o changelog do GoRouter para recursos específicos por versão
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Você saberia encontrar o bug em Flutter?
Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Escrito por
Anthony Fillion-MailletFundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 23 de agosto de 2026
Tags
Compartilhar
Artigos relacionados

Otimização de desempenho no Flutter em 2026: Impeller, reconstruções e boas práticas
Como manter aplicativos Flutter a 60 ou 120 fps constantes em 2026 com Impeller, reconstruções de widgets disciplinadas, RepaintBoundary e profiling no DevTools.

Flutter Impeller em 2026: Arquitetura do motor de renderização, ganhos de performance e perguntas de entrevista
Guia completo sobre Flutter Impeller em 2026: arquitetura do motor de renderização, eliminação do shader jank, benchmarks de performance e perguntas técnicas para entrevistas de emprego.

Gerenciamento de Estado no Flutter: Riverpod vs BLoC - Guia Comparativo Completo
Comparação detalhada entre Riverpod 3.x e BLoC 9.x para gerenciamento de estado no Flutter. Arquitetura, retry automático, testabilidade e casos de uso para escolher a melhor solução.