$npx -y skills add flutter/agent-plugins --skill flutter-setup-declarative-routingConfigure 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.
| 1 | # Implementing Routing and Deep Linking |
| 2 | |
| 3 | ## Contents |
| 4 | - [Core Concepts](#core-concepts) |
| 5 | - [Workflow: Initializing the Application and Router](#workflow-initializing-the-application-and-router) |
| 6 | - [Workflow: Configuring Platform Deep Linking](#workflow-configuring-platform-deep-linking) |
| 7 | - [Workflow: Implementing Nested Navigation](#workflow-implementing-nested-navigation) |
| 8 | - [Examples](#examples) |
| 9 | |
| 10 | ## Core Concepts |
| 11 | |
| 12 | Use the `go_router` package for declarative routing in Flutter. It provides a robust API for complex routing scenarios, deep linking, and nested navigation. |
| 13 | |
| 14 | - **GoRouter**: The central configuration object defining the application's route tree. |
| 15 | - **GoRoute**: A standard route mapping a URL path to a Flutter screen. |
| 16 | - **ShellRoute / StatefulShellRoute**: Wraps child routes in a persistent UI shell (e.g., a `BottomNavigationBar`). `StatefulShellRoute` maintains the state of parallel navigation branches. |
| 17 | - **Path URL Strategy**: Removes the default `#` fragment from web URLs, essential for clean deep linking across platforms. |
| 18 | |
| 19 | ## Workflow: Initializing the Application and Router |
| 20 | |
| 21 | Follow this workflow to bootstrap a new Flutter application with `go_router` and configure the root routing mechanism. |
| 22 | |
| 23 | ### Task Progress |
| 24 | - [ ] Create the Flutter application. |
| 25 | - [ ] Add the `go_router` dependency. |
| 26 | - [ ] Configure the URL strategy for web/deep linking. |
| 27 | - [ ] Implement the `GoRouter` configuration. |
| 28 | - [ ] Bind the router to `MaterialApp.router`. |
| 29 | |
| 30 | ### 1. Scaffold the Application |
| 31 | Run the following commands to create the app and add the required routing package: |
| 32 | ```bash |
| 33 | flutter create <app-name> |
| 34 | cd <app-name> |
| 35 | flutter pub add go_router |
| 36 | ``` |
| 37 | |
| 38 | ### 2. Configure the Router |
| 39 | Define a top-level `GoRouter` instance. Handle authentication or state-based routing using the `redirect` parameter. |
| 40 | |
| 41 | ```dart |
| 42 | import 'package:flutter/material.dart'; |
| 43 | import 'package:go_router/go_router.dart'; |
| 44 | import 'package:flutter_web_plugins/url_strategy.dart'; |
| 45 | |
| 46 | void main() { |
| 47 | // Use path URL strategy to remove the '#' from web URLs |
| 48 | usePathUrlStrategy(); |
| 49 | runApp(const MyApp()); |
| 50 | } |
| 51 | |
| 52 | final GoRouter _router = GoRouter( |
| 53 | initialLocation: '/', |
| 54 | routes: [ |
| 55 | GoRoute( |
| 56 | path: '/', |
| 57 | builder: (context, state) => const HomeScreen(), |
| 58 | routes: [ |
| 59 | GoRoute( |
| 60 | path: 'details/:id', |
| 61 | builder: (context, state) => DetailsScreen(id: state.pathParameters['id']!), |
| 62 | ), |
| 63 | ], |
| 64 | ), |
| 65 | ], |
| 66 | errorBuilder: (context, state) => ErrorScreen(error: state.error), |
| 67 | ); |
| 68 | |
| 69 | class MyApp extends StatelessWidget { |
| 70 | const MyApp({super.key}); |
| 71 | |
| 72 | @override |
| 73 | Widget build(BuildContext context) { |
| 74 | return MaterialApp.router( |
| 75 | routerConfig: _router, |
| 76 | title: 'Routing App', |
| 77 | ); |
| 78 | } |
| 79 | } |
| 80 | ``` |
| 81 | |
| 82 | ## Workflow: Configuring Platform Deep Linking |
| 83 | |
| 84 | Configure the native platforms to intercept specific URLs and route them into the Flutter application. |
| 85 | |
| 86 | ### Task Progress |
| 87 | - [ ] Determine target platforms (iOS, Android, or both). |
| 88 | - [ ] Apply conditional configuration for Android (Manifest + Asset Links). |
| 89 | - [ ] Apply conditional configuration for iOS (Plist + Entitlements + AASA). |
| 90 | - [ ] Run validator -> review errors -> fix. |
| 91 | |
| 92 | ### If configuring for Android: |
| 93 | 1. **Modify `AndroidManifest.xml`**: Add the intent filter inside the `<activity>` tag for `.MainActivity`. |
| 94 | ```xml |
| 95 | <intent-filter android:autoVerify="true"> |
| 96 | <action android:name="android.intent.action.VIEW" /> |
| 97 | <category android:name="android.intent.category.DEFAULT" /> |
| 98 | <category android:name="android.intent.category.BROWSABLE" /> |
| 99 | <data android:scheme="http" android:host="yourdomain.com" /> |
| 100 | <data android:scheme="https" /> |
| 101 | </intent-filter> |
| 102 | ``` |
| 103 | 2. **Host `assetlinks.json`**: Serve the following JSON at `https://yourdomain.com/.well-known/assetlinks.json`. |
| 104 | ```json |
| 105 | [{ |
| 106 | "relation": ["delegate_permission/common.handle_all_urls"], |
| 107 | "target": { |
| 108 | "namespace": "android_app", |
| 109 | "package_name": "com.yourcompany.yourapp", |
| 110 | "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"] |
| 111 | } |
| 112 | }] |
| 113 | ``` |
| 114 | |
| 115 | ### If configuring for iOS: |
| 116 | 1. **Modify `Info.plist`**: Opt-in to Flutter's default deep link handler. |
| 117 | *Note: If using a third-party deep linking plugin (e.g., `app_links`), set this to `NO` to prevent conflicts.* |
| 118 | ```xml |
| 119 | <key>FlutterDeepLinkingEnabled</key> |
| 120 | <true/> |
| 121 | ``` |
| 122 | 2. **Modify `Runner.entitlements`**: Add the associated domain. |
| 123 | ```xml |
| 124 | <key>com.apple.developer.associated-domains</key> |
| 125 | <array> |
| 126 | <string>applinks:yourdomain.com</string> |
| 127 | </array> |
| 128 | ``` |
| 129 | 3. **Host `apple-app-site-association`**: Serve the following JSON (without a `.json` extension) at `https://yourdomain.com/.well-known/apple-app-site-association`. |
| 130 | ```json |
| 131 | { |
| 132 | "app |