flutter-setup-declarative-routing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Implementing Routing and Deep Linking

实现路由与深度链接

Contents

目录

Core Concepts

核心概念

Use the
go_router
package for declarative routing in Flutter. It provides a robust API for complex routing scenarios, deep linking, and nested navigation.
  • GoRouter: The central configuration object defining the application's route tree.
  • GoRoute: A standard route mapping a URL path to a Flutter screen.
  • ShellRoute / StatefulShellRoute: Wraps child routes in a persistent UI shell (e.g., a
    BottomNavigationBar
    ).
    StatefulShellRoute
    maintains the state of parallel navigation branches.
  • Path URL Strategy: Removes the default
    #
    fragment from web URLs, essential for clean deep linking across platforms.
在Flutter中使用
go_router
包实现声明式路由。它为复杂路由场景、深度链接和嵌套导航提供了强大的API。
  • GoRouter:定义应用路由树的核心配置对象。
  • GoRoute:标准路由,将URL路径映射到Flutter页面。
  • ShellRoute / StatefulShellRoute:将子路由包裹在持久化UI外壳中(如
    BottomNavigationBar
    )。
    StatefulShellRoute
    可维护并行导航分支的状态。
  • Path URL Strategy:移除Web URL中默认的
    #
    片段,这是跨平台实现清晰深度链接的关键。

Workflow: Initializing the Application and Router

工作流程:初始化应用与路由

Follow this workflow to bootstrap a new Flutter application with
go_router
and configure the root routing mechanism.
按照以下流程,使用
go_router
启动新的Flutter应用并配置根路由机制。

Task Progress

任务进度

  • Create the Flutter application.
  • Add the
    go_router
    dependency.
  • Configure the URL strategy for web/deep linking.
  • Implement the
    GoRouter
    configuration.
  • Bind the router to
    MaterialApp.router
    .
  • 创建Flutter应用。
  • 添加
    go_router
    依赖。
  • 为Web/深度链接配置URL策略。
  • 实现
    GoRouter
    配置。
  • 将路由绑定到
    MaterialApp.router

1. Scaffold the Application

1. 搭建应用框架

Run the following commands to create the app and add the required routing package:
bash
flutter create <app-name>
cd <app-name>
flutter pub add go_router
运行以下命令创建应用并添加所需的路由包:
bash
flutter create <app-name>
cd <app-name>
flutter pub add go_router

2. Configure the Router

2. 配置路由

Define a top-level
GoRouter
instance. Handle authentication or state-based routing using the
redirect
parameter.
dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_web_plugins/url_strategy.dart';

void main() {
  // Use path URL strategy to remove the '#' from web URLs
  usePathUrlStrategy();
  runApp(const MyApp());
}

final GoRouter _router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'details/:id',
          builder: (context, state) => DetailsScreen(id: state.pathParameters['id']!),
        ),
      ],
    ),
  ],
  errorBuilder: (context, state) => ErrorScreen(error: state.error),
);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
      title: 'Routing App',
    );
  }
}
定义顶层
GoRouter
实例。使用
redirect
参数处理身份验证或基于状态的路由。
dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_web_plugins/url_strategy.dart';

void main() {
  // 使用路径URL策略移除Web URL中的'#'
  usePathUrlStrategy();
  runApp(const MyApp());
}

final GoRouter _router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'details/:id',
          builder: (context, state) => DetailsScreen(id: state.pathParameters['id']!),
        ),
      ],
    ),
  ],
  errorBuilder: (context, state) => ErrorScreen(error: state.error),
);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
      title: 'Routing App',
    );
  }
}

Workflow: Configuring Platform Deep Linking

工作流程:配置平台深度链接

Configure the native platforms to intercept specific URLs and route them into the Flutter application.
配置原生平台以拦截特定URL并将其路由到Flutter应用中。

Task Progress

任务进度

  • Determine target platforms (iOS, Android, or both).
  • Apply conditional configuration for Android (Manifest + Asset Links).
  • Apply conditional configuration for iOS (Plist + Entitlements + AASA).
  • Run validator -> review errors -> fix.
  • 确定目标平台(iOS、Android或两者)。
  • 为Android应用条件配置(Manifest + Asset Links)。
  • 为iOS应用条件配置(Plist + Entitlements + AASA)。
  • 运行验证器 -> 查看错误 -> 修复问题。

If configuring for Android:

若为Android配置:

  1. Modify
    AndroidManifest.xml
    : Add the intent filter inside the
    <activity>
    tag for
    .MainActivity
    .
xml
<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="http" android:host="yourdomain.com" />
    <data android:scheme="https" />
</intent-filter>
  1. Host
    assetlinks.json
    : Serve the following JSON at
    https://yourdomain.com/.well-known/assetlinks.json
    .
json
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.yourcompany.yourapp",
    "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"]
  }
}]
  1. 修改
    AndroidManifest.xml
    :在
    .MainActivity
    <activity>
    标签内添加意图过滤器。
xml
<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="http" android:host="yourdomain.com" />
    <data android:scheme="https" />
</intent-filter>
  1. 托管
    assetlinks.json
    :在
    https://yourdomain.com/.well-known/assetlinks.json
    地址提供以下JSON内容。
json
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.yourcompany.yourapp",
    "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"]
  }
}]

If configuring for iOS:

若为iOS配置:

  1. Modify
    Info.plist
    : Opt-in to Flutter's default deep link handler. Note: If using a third-party deep linking plugin (e.g.,
    app_links
    ), set this to
    NO
    to prevent conflicts.
xml
<key>FlutterDeepLinkingEnabled</key>
<true/>
  1. Modify
    Runner.entitlements
    : Add the associated domain.
xml
<key>com.apple.developer.associated-domains</key>
<array>
  <string>applinks:yourdomain.com</string>
</array>
  1. Host
    apple-app-site-association
    : Serve the following JSON (without a
    .json
    extension) at
    https://yourdomain.com/.well-known/apple-app-site-association
    .
json
{
  "applinks": {
    "apps": [],
    "details": [{
      "appIDs": ["TEAM_ID.com.yourcompany.yourapp"],
      "paths": ["*"],
      "components": [{"/": "/*"}]
    }]
  }
}
  1. 修改
    Info.plist
    :启用Flutter默认的深度链接处理器。 注意:如果使用第三方深度链接插件(如
    app_links
    ),请将此值设为
    NO
    以避免冲突。
xml
<key>FlutterDeepLinkingEnabled</key>
<true/>
  1. 修改
    Runner.entitlements
    :添加关联域名。
xml
<key>com.apple.developer.associated-domains</key>
<array>
  <string>applinks:yourdomain.com</string>
</array>
  1. 托管
    apple-app-site-association
    :在
    https://yourdomain.com/.well-known/apple-app-site-association
    地址提供以下JSON内容(无需
    .json
    扩展名)。
json
{
  "applinks": {
    "apps": [],
    "details": [{
      "appIDs": ["TEAM_ID.com.yourcompany.yourapp"],
      "paths": ["*"],
      "components": [{"/": "/*"}]
    }]
  }
}

Validation Loop

验证循环

Run validator -> review errors -> fix.
  • Android: Test using ADB.
    bash
    adb shell 'am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://yourdomain.com/details/123"' com.yourcompany.yourapp
  • iOS: Test using
    xcrun
    on a booted simulator.
    bash
    xcrun simctl openurl booted https://yourdomain.com/details/123
运行验证器 -> 查看错误 -> 修复问题。
  • Android:使用ADB测试。
    bash
    adb shell 'am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://yourdomain.com/details/123"' com.yourcompany.yourapp
  • iOS:在已启动的模拟器上使用
    xcrun
    测试。
    bash
    xcrun simctl openurl booted https://yourdomain.com/details/123

Workflow: Implementing Nested Navigation

工作流程:实现嵌套导航

Use
StatefulShellRoute
to implement persistent UI shells (like a bottom navigation bar) that maintain the state of their child routes.
使用
StatefulShellRoute
实现持久化UI外壳(如底部导航栏),以维护子路由的状态。

Task Progress

任务进度

  • Define
    StatefulShellRoute.indexedStack
    in the
    GoRouter
    configuration.
  • Create
    StatefulShellBranch
    instances for each navigation tab.
  • Implement the shell widget using
    StatefulNavigationShell
    .
dart
final GoRouter _router = GoRouter(
  initialLocation: '/home',
  routes: [
    StatefulShellRoute.indexedStack(
      builder: (context, state, navigationShell) {
        return ScaffoldWithNavBar(navigationShell: navigationShell);
      },
      branches: [
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/home',
              builder: (context, state) => const HomeScreen(),
            ),
          ],
        ),
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/settings',
              builder: (context, state) => const SettingsScreen(),
            ),
          ],
        ),
      ],
    ),
  ],
);
  • GoRouter
    配置中定义
    StatefulShellRoute.indexedStack
  • 为每个导航标签创建
    StatefulShellBranch
    实例。
  • 使用
    StatefulNavigationShell
    实现外壳组件。
dart
final GoRouter _router = GoRouter(
  initialLocation: '/home',
  routes: [
    StatefulShellRoute.indexedStack(
      builder: (context, state, navigationShell) {
        return ScaffoldWithNavBar(navigationShell: navigationShell);
      },
      branches: [
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/home',
              builder: (context, state) => const HomeScreen(),
            ),
          ],
        ),
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/settings',
              builder: (context, state) => const SettingsScreen(),
            ),
          ],
        ),
      ],
    ),
  ],
);

Examples

示例

High-Fidelity Shell Widget Implementation

高保真外壳组件实现

Implement the UI shell that consumes the
StatefulNavigationShell
to handle branch switching.
dart
class ScaffoldWithNavBar extends StatelessWidget {
  const ScaffoldWithNavBar({
    required this.navigationShell,
    super.key,
  });

  final StatefulNavigationShell navigationShell;

  void _goBranch(int index) {
    navigationShell.goBranch(
      index,
      // Support navigating to the initial location when tapping the active tab.
      initialLocation: index == navigationShell.currentIndex,
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: navigationShell,
      bottomNavigationBar: NavigationBar(
        selectedIndex: navigationShell.currentIndex,
        onDestinationSelected: _goBranch,
        destinations: const [
          NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
          NavigationDestination(icon: Icon(Icons.settings), label: 'Settings'),
        ],
      ),
    );
  }
}
实现一个UI外壳,通过
StatefulNavigationShell
处理分支切换。
dart
class ScaffoldWithNavBar extends StatelessWidget {
  const ScaffoldWithNavBar({
    required this.navigationShell,
    super.key,
  });

  final StatefulNavigationShell navigationShell;

  void _goBranch(int index) {
    navigationShell.goBranch(
      index,
      // 支持点击当前标签时导航到初始位置
      initialLocation: index == navigationShell.currentIndex,
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: navigationShell,
      bottomNavigationBar: NavigationBar(
        selectedIndex: navigationShell.currentIndex,
        onDestinationSelected: _goBranch,
        destinations: const [
          NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
          NavigationDestination(icon: Icon(Icons.settings), label: 'Settings'),
        ],
      ),
    );
  }
}

Programmatic Navigation

程序化导航

Use the
context.go()
and
context.push()
extension methods provided by
go_router
.
dart
// Replaces the current route stack with the target route (Declarative)
context.go('/details/123');

// Pushes the target route onto the existing stack (Imperative)
context.push('/details/123');

// Navigates using a named route and path parameters
context.goNamed('details', pathParameters: {'id': '123'});

// Pops the current route
context.pop();
使用
go_router
提供的
context.go()
context.push()
扩展方法。
dart
// 用目标路由替换当前路由栈(声明式)
context.go('/details/123');

// 将目标路由推入现有栈(命令式)
context.push('/details/123');

// 使用命名路由和路径参数导航
context.goNamed('details', pathParameters: {'id': '123'});

// 弹出当前路由
context.pop();