flutter-setup-declarative-routing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImplementing Routing and Deep Linking
实现路由与深度链接
Contents
目录
Core Concepts
核心概念
Use the package for declarative routing in Flutter. It provides a robust API for complex routing scenarios, deep linking, and nested navigation.
go_router- 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 ).
BottomNavigationBarmaintains the state of parallel navigation branches.StatefulShellRoute - Path URL Strategy: Removes the default fragment from web URLs, essential for clean deep linking across platforms.
#
在Flutter中使用包实现声明式路由。它为复杂路由场景、深度链接和嵌套导航提供了强大的API。
go_router- 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 and configure the root routing mechanism.
go_router按照以下流程,使用启动新的Flutter应用并配置根路由机制。
go_routerTask Progress
任务进度
- Create the Flutter application.
- Add the dependency.
go_router - Configure the URL strategy for web/deep linking.
- Implement the configuration.
GoRouter - 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_router2. Configure the Router
2. 配置路由
Define a top-level instance. Handle authentication or state-based routing using the parameter.
GoRouterredirectdart
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',
);
}
}定义顶层实例。使用参数处理身份验证或基于状态的路由。
GoRouterredirectdart
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配置:
- Modify : Add the intent filter inside the
AndroidManifest.xmltag for<activity>..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>- Host : Serve the following JSON at
assetlinks.json.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"]
}
}]- 修改:在
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>- 托管:在
assetlinks.json地址提供以下JSON内容。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"]
}
}]If configuring for iOS:
若为iOS配置:
- Modify : Opt-in to Flutter's default deep link handler. Note: If using a third-party deep linking plugin (e.g.,
Info.plist), set this toapp_linksto prevent conflicts.NO
xml
<key>FlutterDeepLinkingEnabled</key>
<true/>- Modify : Add the associated domain.
Runner.entitlements
xml
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:yourdomain.com</string>
</array>- Host : Serve the following JSON (without a
apple-app-site-associationextension) at.json.https://yourdomain.com/.well-known/apple-app-site-association
json
{
"applinks": {
"apps": [],
"details": [{
"appIDs": ["TEAM_ID.com.yourcompany.yourapp"],
"paths": ["*"],
"components": [{"/": "/*"}]
}]
}
}- 修改:启用Flutter默认的深度链接处理器。 注意:如果使用第三方深度链接插件(如
Info.plist),请将此值设为app_links以避免冲突。NO
xml
<key>FlutterDeepLinkingEnabled</key>
<true/>- 修改:添加关联域名。
Runner.entitlements
xml
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:yourdomain.com</string>
</array>- 托管:在
apple-app-site-association地址提供以下JSON内容(无需https://yourdomain.com/.well-known/apple-app-site-association扩展名)。.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 on a booted simulator.
xcrunbashxcrun 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:在已启动的模拟器上使用测试。
xcrunbashxcrun simctl openurl booted https://yourdomain.com/details/123
Workflow: Implementing Nested Navigation
工作流程:实现嵌套导航
Use to implement persistent UI shells (like a bottom navigation bar) that maintain the state of their child routes.
StatefulShellRoute使用实现持久化UI外壳(如底部导航栏),以维护子路由的状态。
StatefulShellRouteTask Progress
任务进度
- Define in the
StatefulShellRoute.indexedStackconfiguration.GoRouter - Create instances for each navigation tab.
StatefulShellBranch - 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 to handle branch switching.
StatefulNavigationShelldart
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外壳,通过处理分支切换。
StatefulNavigationShelldart
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 and extension methods provided by .
context.go()context.push()go_routerdart
// 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_routercontext.go()context.push()dart
// 用目标路由替换当前路由栈(声明式)
context.go('/details/123');
// 将目标路由推入现有栈(命令式)
context.push('/details/123');
// 使用命名路由和路径参数导航
context.goNamed('details', pathParameters: {'id': '123'});
// 弹出当前路由
context.pop();