Flutter Navigation 2.0 und GoRouter 2026: Deep Linking und Interviewfragen
Flutter-Navigation mit GoRouter 17.5 meistern: Deklaratives Routing, Deep Linking, ShellRoute, Route Guards und häufige Interviewfragen mit praktischen Beispielen.

Die Flutter-Navigation hat sich seit der Einführung von Navigation 2.0 erheblich weiterentwickelt. GoRouter 17.5, das offizielle Paket des Flutter-Teams, übernimmt die Komplexität der Router-API und bietet deklaratives Routing, automatisches Deep Linking und verschachtelte Navigation standardmäßig.
Das Flutter-Team betrachtet GoRouter seit 2026 als feature-complete. Es unterstützt Pfadparameter, Query-Parameter, Redirects, ShellRoute für persistente UI und StatefulShellRoute zur Bewahrung des Tab-Zustands. Raw Navigator 2.0 wird in Produktionsumgebungen kaum noch manuell implementiert.
Die Architektur von Navigator 2.0 verstehen
Navigator 2.0 führte einen deklarativen, URL-gesteuerten Ansatz für die Flutter-Navigation ein. Anstatt imperativer push- und pop-Aufrufe bestimmt der App-Zustand, was auf dem Navigationsstapel erscheint. Die Architektur basiert auf drei Kernklassen:
- Router: Das Top-Level-Widget, das die Navigation koordiniert
- RouteInformationParser: Übersetzt URLs in App-Zustand
- RouterDelegate: Baut den Widget-Baum basierend auf diesem Zustand
Wenn ein Deep Link vom Betriebssystem eintrifft oder sich eine URL im Webbrowser ändert, aktualisiert das Framework den Zustand, und die Benutzeroberfläche reagiert entsprechend. Das Problem: Das manuelle Schreiben eines benutzerdefinierten RouterDelegate und RouteInformationParser erfordert erheblichen Boilerplate-Code.
// 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();
}
}Diese Ausführlichkeit ist der Grund für die Existenz von GoRouter. Es kapselt Navigator 2.0 und übernimmt den Boilerplate-Code.
GoRouter 17.5 einrichten
GoRouter 17.5 führt Unterstützung für Routen-Metadaten und reguläre Ausdrücke als Einschränkungen für Pfadparameter ein. Die Mindestanforderung ist Flutter 3.32 und 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);
},
),
],
),
],
);Der Router integriert sich mit 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',
);
}
}Deep Linking-Konfiguration für Android und iOS
Deep Linking ermöglicht es externen URLs, bestimmte Bildschirme in der App zu öffnen. GoRouter übernimmt das Routing automatisch, sobald die Plattformkonfiguration vorhanden ist.
Android App Links
Android erfordert einen intent-filter in der AndroidManifest.xml und eine Digital Asset Links-Datei, die auf der Domain gehostet wird:
<!-- 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>Die assetlinks.json-Datei muss unter https://example.com/.well-known/assetlinks.json bereitgestellt werden.
iOS Universal Links
Für iOS muss die Capability Associated Domains in Xcode aktiviert werden, und eine apple-app-site-association-Datei muss gehostet werden:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAMID.com.example.app",
"paths": ["/products/*", "/search"]
}
]
}
}GoRouter 17.5 hat einen kritischen Bug behoben, bei dem Android-Deep-Links beim Kaltstart mit leeren Pfaden Schema und Authority verloren. Das bedeutet, dass Deep Links jetzt zuverlässig funktionieren, auch wenn die App nicht läuft.
Android-Deep-Links können mit adb shell am start -a android.intent.action.VIEW -d "https://example.com/products/123" getestet werden. Für iOS: xcrun simctl openurl booted "https://example.com/products/123".
ShellRoute für persistente Navigations-UI
ShellRoute umhüllt untergeordnete Routen mit einem persistenten UI-Element wie einer BottomNavigationBar oder einem Drawer. Die Shell bleibt sichtbar, während zwischen ihren Kindern navigiert wird.
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');
}
}
}GoRouter 17.5 hat ein Problem behoben, bei dem iOS-Zurückgesten die gesamte ShellRoute anstatt nur der aktiven Unterroute entfernten.
Bereit für deine Flutter-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
StatefulShellRoute zur Bewahrung des Tab-Zustands
Wenn Benutzer zwischen Tabs wechseln, baut ShellRoute das Kind neu auf. StatefulShellRoute bewahrt den Navigationsstapel jedes Zweigs:
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(),
),
],
),
],
),
],
);Mit dieser Konfiguration: Wenn ein Benutzer zu /home/details/42 navigiert, zum Explore-Tab wechselt und dann zu Home zurückkehrt, befindet er sich immer noch auf dem Details-Bildschirm.
Route Guards mit Redirect
Redirects behandeln Authentifizierungsprüfungen und bedingte Navigation. Der redirect-Callback wird vor jeder Navigation ausgeführt:
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(),
),
],
);GoRouter 17.5 hat Unterstützung für die Verwendung von Context-Erweiterungsmethoden wie context.namedLocation() innerhalb von Redirect-Callbacks hinzugefügt.
Häufige Flutter-Navigations-Interviewfragen
Interviewer testen häufig Navigationswissen, da es Architektur, Zustandsverwaltung und Plattformintegration berührt. Hier sind Fragen, die Senior-Kandidaten unterscheiden:
F: Welches Problem löst Navigator 2.0, das Navigator 1.0 nicht löst?
Navigator 1.0 verwendet imperative Navigation: Navigator.push() und Navigator.pop(). Die App kann den Navigationszustand nicht als URL darstellen, was Deep Linking und Web-Unterstützung erschwert. Navigator 2.0 ist deklarativ: Der App-Zustand bestimmt den Navigationsstapel, URLs sind erstklassige Bürger, und Deep Links funktionieren automatisch.
F: Wann würde man context.go() vs context.push() verwenden?
context.go('/path') ersetzt den aktuellen Navigationsstapel bis zur übereinstimmenden Route. context.push('/path') fügt eine neue Route auf dem bestehenden Stapel hinzu. Man verwendet go() für Top-Level-Navigation (Tab-Wechsel, zur Startseite gehen) und push() für das Eintauchen in Details unter Beibehaltung der Zurück-Navigation.
F: Wie bewahrt man den Zustand über Tab-Wechsel hinweg?
Man verwendet StatefulShellRoute.indexedStack(). Es erhält separate Navigationsstapel für jeden Zweig und bewahrt sie beim Tab-Wechsel. Ohne diese Funktion wird beim Tab-Wechsel der gesamte Unterbaum neu aufgebaut.
F: Wie übergibt man komplexe Objekte zwischen Routen?
Es existieren drei Ansätze. Erstens: Eine ID im Pfadparameter übergeben und das Objekt auf dem Zielbildschirm abrufen. Zweitens: GoRouters extra-Parameter verwenden, um das Objekt direkt zu übergeben. Drittens: Das Objekt in einer State-Management-Lösung (Riverpod, Bloc) speichern und vom Ziel aus darauf zugreifen. Der erste Ansatz eignet sich am besten für Deep Linking, da die URL teilbar bleibt.
// 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);
},
),F: Was ist der onEnter-Callback in GoRouter 17.5?
Der onEnter-Callback wird beim Betreten einer Route ausgeführt und bietet Zugriff auf sowohl den aktuellen als auch den nächsten Routenzustand. Er ermöglicht die Ausführung von Logik, bevor die Route vollständig lädt, nützlich für Analytics oder bedingtes Vorladen.
Type-Safe Routes mit Code-Generierung
GoRouter unterstützt typsicheres Routing durch Code-Generierung. Man definiert Routen-Datenklassen und lässt build_runner den Navigationscode generieren:
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);
}Nach Ausführung von dart run build_runner build kann mit Compile-Time-Sicherheit navigiert werden:
// Type-safe navigation
ProductRoute(id: '123').go(context);Dies eliminiert Tippfehler in Routenpfaden und stellt sicher, dass Parameter immer bereitgestellt werden.
Flutter-Navigation für Produktions-Apps meistern
- GoRouter 17.5 ist die empfohlene Navigationslösung für Flutter-Apps, die Deep Linking, verschachtelte Navigation oder Web-Unterstützung erfordern
- ShellRoute wird für persistente Bottom-Navigation-Bars verwendet; StatefulShellRoute bewahrt den Navigationsstapel jedes Tabs
- Plattformspezifisches Deep Linking (Android App Links, iOS Universal Links) wird separat von GoRouter konfiguriert, das die Routing-Logik übernimmt
- Authentifizierungs-Guards werden mit dem
redirect-Callback auf Router-Ebene oder einzelner Routen-Ebene implementiert - Für Interview-Szenarien liegt der Fokus auf der Erklärung des deklarativen Modells: Der App-Zustand steuert die UI, URLs werden vom Zustand abgeleitet, und GoRouter übernimmt die Übersetzung
- Typsichere Routen mit Code-Generierung eliminieren Laufzeitfehler durch falsch geschriebene Pfade
- Die Flutter-Navigationsdokumentation und das GoRouter-Changelog enthalten versionsspezifische Features
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Findest du den Bug in Flutter?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 23. August 2026
Tags
Teilen
Verwandte Artikel

Flutter-Performance-Optimierung 2026: Impeller, Rebuilds und Best Practices
Wie Flutter-Apps 2026 konstante 60 oder 120 fps halten: mit Impeller, disziplinierten Widget-Rebuilds, RepaintBoundary und DevTools-Profiling.

Flutter Custom RenderObjects 2026: Eigene Zeichenlogik und Interview-Fragen
Ein tiefgehender Leitfaden zu Flutters Rendering-Pipeline mit benutzerdefinierten RenderObjects. Erfahren Sie, wann CustomPainter oder RenderBox verwendet werden sollte und bereiten Sie sich auf Senior-Level Interviews vor.

React Native vs. Flutter 2026: Architektur und Performance im Vergleich
Ein umfassender Vergleich zwischen React Native und Flutter im Jahr 2026: Architektur, Performance, Entwicklererfahrung und Anwendungsfälle für die plattformübergreifende mobile Entwicklung.