main
md 255 lines 8.01 KB
Rendered Raw
1 ---
2 name: flutter-setup-declarative-routing
3 description: Configure `MaterialApp.router` using a package like `go_router` for advanced URL-based navigation. Use when developing web applications or mobile apps that require specific deep linking and browser history support.
4 metadata:
5 model: models/gemini-3.1-pro-preview
6 last_modified: Tue, 21 Apr 2026 21:08:03 GMT
7 ---
8 # Implementing Routing and Deep Linking
9
10 ## Contents
11 - [Core Concepts](#core-concepts)
12 - [Workflow: Initializing the Application and Router](#workflow-initializing-the-application-and-router)
13 - [Workflow: Configuring Platform Deep Linking](#workflow-configuring-platform-deep-linking)
14 - [Workflow: Implementing Nested Navigation](#workflow-implementing-nested-navigation)
15 - [Examples](#examples)
16
17 ## Core Concepts
18
19 Use the `go_router` package for declarative routing in Flutter. It provides a robust API for complex routing scenarios, deep linking, and nested navigation.
20
21 - **GoRouter**: The central configuration object defining the application's route tree.
22 - **GoRoute**: A standard route mapping a URL path to a Flutter screen.
23 - **ShellRoute / StatefulShellRoute**: Wraps child routes in a persistent UI shell (e.g., a `BottomNavigationBar`). `StatefulShellRoute` maintains the state of parallel navigation branches.
24 - **Path URL Strategy**: Removes the default `#` fragment from web URLs, essential for clean deep linking across platforms.
25
26 ## Workflow: Initializing the Application and Router
27
28 Follow this workflow to bootstrap a new Flutter application with `go_router` and configure the root routing mechanism.
29
30 ### Task Progress
31 - [ ] Create the Flutter application.
32 - [ ] Add the `go_router` dependency.
33 - [ ] Configure the URL strategy for web/deep linking.
34 - [ ] Implement the `GoRouter` configuration.
35 - [ ] Bind the router to `MaterialApp.router`.
36
37 ### 1. Scaffold the Application
38 Run the following commands to create the app and add the required routing package:
39 ```bash
40 flutter create <app-name>
41 cd <app-name>
42 flutter pub add go_router
43 ```
44
45 ### 2. Configure the Router
46 Define a top-level `GoRouter` instance. Handle authentication or state-based routing using the `redirect` parameter.
47
48 ```dart
49 import 'package:flutter/material.dart';
50 import 'package:go_router/go_router.dart';
51 import 'package:flutter_web_plugins/url_strategy.dart';
52
53 void main() {
54 // Use path URL strategy to remove the '#' from web URLs
55 usePathUrlStrategy();
56 runApp(const MyApp());
57 }
58
59 final GoRouter _router = GoRouter(
60 initialLocation: '/',
61 routes: [
62 GoRoute(
63 path: '/',
64 builder: (context, state) => const HomeScreen(),
65 routes: [
66 GoRoute(
67 path: 'details/:id',
68 builder: (context, state) => DetailsScreen(id: state.pathParameters['id']!),
69 ),
70 ],
71 ),
72 ],
73 errorBuilder: (context, state) => ErrorScreen(error: state.error),
74 );
75
76 class MyApp extends StatelessWidget {
77 const MyApp({super.key});
78
79 @override
80 Widget build(BuildContext context) {
81 return MaterialApp.router(
82 routerConfig: _router,
83 title: 'Routing App',
84 );
85 }
86 }
87 ```
88
89 ## Workflow: Configuring Platform Deep Linking
90
91 Configure the native platforms to intercept specific URLs and route them into the Flutter application.
92
93 ### Task Progress
94 - [ ] Determine target platforms (iOS, Android, or both).
95 - [ ] Apply conditional configuration for Android (Manifest + Asset Links).
96 - [ ] Apply conditional configuration for iOS (Plist + Entitlements + AASA).
97 - [ ] Run validator -> review errors -> fix.
98
99 ### If configuring for Android:
100 1. **Modify `AndroidManifest.xml`**: Add the intent filter inside the `<activity>` tag for `.MainActivity`.
101 ```xml
102 <intent-filter android:autoVerify="true">
103 <action android:name="android.intent.action.VIEW" />
104 <category android:name="android.intent.category.DEFAULT" />
105 <category android:name="android.intent.category.BROWSABLE" />
106 <data android:scheme="http" android:host="yourdomain.com" />
107 <data android:scheme="https" />
108 </intent-filter>
109 ```
110 2. **Host `assetlinks.json`**: Serve the following JSON at `https://yourdomain.com/.well-known/assetlinks.json`.
111 ```json
112 [{
113 "relation": ["delegate_permission/common.handle_all_urls"],
114 "target": {
115 "namespace": "android_app",
116 "package_name": "com.yourcompany.yourapp",
117 "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"]
118 }
119 }]
120 ```
121
122 ### If configuring for iOS:
123 1. **Modify `Info.plist`**: Opt-in to Flutter's default deep link handler.
124 *Note: If using a third-party deep linking plugin (e.g., `app_links`), set this to `NO` to prevent conflicts.*
125 ```xml
126 <key>FlutterDeepLinkingEnabled</key>
127 <true/>
128 ```
129 2. **Modify `Runner.entitlements`**: Add the associated domain.
130 ```xml
131 <key>com.apple.developer.associated-domains</key>
132 <array>
133 <string>applinks:yourdomain.com</string>
134 </array>
135 ```
136 3. **Host `apple-app-site-association`**: Serve the following JSON (without a `.json` extension) at `https://yourdomain.com/.well-known/apple-app-site-association`.
137 ```json
138 {
139 "applinks": {
140 "apps": [],
141 "details": [{
142 "appIDs": ["TEAM_ID.com.yourcompany.yourapp"],
143 "paths": ["*"],
144 "components": [{"/": "/*"}]
145 }]
146 }
147 }
148 ```
149
150 ### Validation Loop
151 Run validator -> review errors -> fix.
152 - **Android**: Test using ADB.
153 ```bash
154 adb shell 'am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://yourdomain.com/details/123"' com.yourcompany.yourapp
155 ```
156 - **iOS**: Test using `xcrun` on a booted simulator.
157 ```bash
158 xcrun simctl openurl booted https://yourdomain.com/details/123
159 ```
160
161 ## Workflow: Implementing Nested Navigation
162
163 Use `StatefulShellRoute` to implement persistent UI shells (like a bottom navigation bar) that maintain the state of their child routes.
164
165 ### Task Progress
166 - [ ] Define `StatefulShellRoute.indexedStack` in the `GoRouter` configuration.
167 - [ ] Create `StatefulShellBranch` instances for each navigation tab.
168 - [ ] Implement the shell widget using `StatefulNavigationShell`.
169
170 ```dart
171 final GoRouter _router = GoRouter(
172 initialLocation: '/home',
173 routes: [
174 StatefulShellRoute.indexedStack(
175 builder: (context, state, navigationShell) {
176 return ScaffoldWithNavBar(navigationShell: navigationShell);
177 },
178 branches: [
179 StatefulShellBranch(
180 routes: [
181 GoRoute(
182 path: '/home',
183 builder: (context, state) => const HomeScreen(),
184 ),
185 ],
186 ),
187 StatefulShellBranch(
188 routes: [
189 GoRoute(
190 path: '/settings',
191 builder: (context, state) => const SettingsScreen(),
192 ),
193 ],
194 ),
195 ],
196 ),
197 ],
198 );
199 ```
200
201 ## Examples
202
203 ### High-Fidelity Shell Widget Implementation
204 Implement the UI shell that consumes the `StatefulNavigationShell` to handle branch switching.
205
206 ```dart
207 class ScaffoldWithNavBar extends StatelessWidget {
208 const ScaffoldWithNavBar({
209 required this.navigationShell,
210 super.key,
211 });
212
213 final StatefulNavigationShell navigationShell;
214
215 void _goBranch(int index) {
216 navigationShell.goBranch(
217 index,
218 // Support navigating to the initial location when tapping the active tab.
219 initialLocation: index == navigationShell.currentIndex,
220 );
221 }
222
223 @override
224 Widget build(BuildContext context) {
225 return Scaffold(
226 body: navigationShell,
227 bottomNavigationBar: NavigationBar(
228 selectedIndex: navigationShell.currentIndex,
229 onDestinationSelected: _goBranch,
230 destinations: const [
231 NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
232 NavigationDestination(icon: Icon(Icons.settings), label: 'Settings'),
233 ],
234 ),
235 );
236 }
237 }
238 ```
239
240 ### Programmatic Navigation
241 Use the `context.go()` and `context.push()` extension methods provided by `go_router`.
242
243 ```dart
244 // Replaces the current route stack with the target route (Declarative)
245 context.go('/details/123');
246
247 // Pushes the target route onto the existing stack (Imperative)
248 context.push('/details/123');
249
250 // Navigates using a named route and path parameters
251 context.goNamed('details', pathParameters: {'id': '123'});
252
253 // Pops the current route
254 context.pop();
255 ```