SKILL.md
唯讀
名稱
flutter-setup-declarative-routing
描述
使用 `go_router` 等套件來設定 `MaterialApp.router`,實現進階的 URL 路由導覽。適用於需要特定深度連結(Deep Linking)與瀏覽器歷史紀錄支援的 Web 應用程式或行動 App 開發情境。
實作路由與深度連結
目錄
核心概念
在 Flutter 中推薦使用 go_router 套件進行宣告式路由(Declarative Routing)。它提供了強大的 API,能輕鬆處理複雜的路由情境、深度連結與巢狀導覽(Nested Navigation)。
- GoRouter:定義應用程式路由樹的核心設定物件。
- GoRoute:標準路由,負責將特定的 URL 路徑映射至指定的 Flutter 畫面(Screen)。
- ShellRoute / StatefulShellRoute:將子路由包覆在常駐的 UI 外殼中(例如
BottomNavigationBar)。其中StatefulShellRoute能保持各個平行導覽分支的狀態。 - Path URL Strategy:移除 Web URL 中預設的
#標籤,這對於在跨平台上實現乾淨的深度連結至關重要。
工作流程:初始化應用程式與路由器
請遵循以下工作流程,使用 go_router 初始化全新的 Flutter 應用程式並設定根路由機制。
任務進度
- [ ] 建立 Flutter 應用程式。
- [ ] 新增
go_router套件相依性。 - [ ] 設定 Web / 深度連結的 URL 策略。
- [ ] 實作
GoRouter設定。 - [ ] 將路由器綁定至
MaterialApp.router。
1. 建立應用程式框架
執行以下命令建立 App 並新增所需的路由套件:
flutter create <app-name>
cd <app-name>
flutter pub add go_router
2. 設定路由器
定義全域(Top-level)的 GoRouter 實例。可使用 redirect 參數來處理身份驗證或基於狀態的路由跳轉。
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
void main() {
// 使用 path 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});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
title: 'Routing App',
);
}
}
工作流程:設定跨平台深度連結
設定原生平台以攔截特定的 URL,並將其引導至 Flutter 應用程式中對應的路由。
任務進度
- [ ] 確認目標平台(iOS、Android 或兩者皆是)。
- [ ] 套用 Android 條件設定(Manifest + Asset Links)。
- [ ] 套用 iOS 條件設定(Plist + Entitlements + AASA)。
- [ ] 執行驗證工具 -> 檢視錯誤 -> 修復問題。
若為 Android 進行設定:
- 修改
AndroidManifest.xml:在.MainActivity的<activity>標籤內新增 Intent Filter。
<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:在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"]
}
}]
若為 iOS 進行設定:
- 修改
Info.plist:啟用 Flutter 預設的深度連結處理器。
注意:若使用第三方深度連結外掛(如app_links),請將此項設為NO以免發生衝突。
<key>FlutterDeepLinkingEnabled</key>
<true/>
- 修改
Runner.entitlements:新增關聯網域(Associated Domain)。
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:yourdomain.com</string>
</array>
- 部署
apple-app-site-association:在https://yourdomain.com/.well-known/apple-app-site-association提供以下 JSON 內容(副檔名不可為.json)。
{
"applinks": {
"apps": [],
"details": [{
"appIDs": ["TEAM_ID.com.yourcompany.yourapp"],
"paths": ["*"],
"components": [{"/": "/*"}]
}]
}
}
驗證流程
執行驗證工具 -> 檢視錯誤 -> 修復問題。
- Android:使用 ADB 進行測試。
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進行測試。xcrun simctl openurl booted https://yourdomain.com/details/123
工作流程:實作巢狀導覽
使用 StatefulShellRoute 實作常駐的 UI 外殼(如底部導覽列),並維持其子路由的狀態。
任務進度
- [ ] 在
GoRouter設定中定義StatefulShellRoute.indexedStack。 - [ ] 為每個導覽分頁建立
StatefulShellBranch實例。 - [ ] 使用
StatefulNavigationShell實作外殼 Widget。
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(),
),
],
),
],
),
],
);
程式碼範例
高完整度 Outer Shell Widget 實作
實作接收 StatefulNavigationShell 並處理分支切換的 UI 外殼。
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,
);
}
@override
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'),
],
),
);
}
}
程式化導覽
使用 go_router 提供的 context.go() 與 context.push() 擴充方法。
// 以目標路由取代目前的路由堆疊(宣告式)
context.go('/details/123');
// 將目標路由推入現有的堆疊頂層(命令式)
context.push('/details/123');
// 使用具名路由與路徑參數進行導覽
context.goNamed('details', pathParameters: {'id': '123'});
// 返回 / 關閉(Pop)目前路由
context.pop();






