Flutter: go_router 入门(面向 Android 开发者)
目标:用 Android 的路由思维,快速上手 Flutter 的 go_router,从依赖引入 → 初始化 → 路由定义 → 导航与参数 → 守卫/重定向 → 嵌套路由/ShellRoute → 错误页与深链。
1. 依赖引入(pubspec.yaml)
你的项目已存在 go_router(版本可能较低)。建议与现有项目版本保持一致;若新建工程,可使用较新版本:
yaml
dependencies:
go_router: ^14.2.0 # 或与你项目一致的版本
bash
flutter pub get
2. 初始化 Router(在应用入口)
dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() => runApp(const MyApp());
final GoRouter _router = GoRouter(
initialLocation: '/',
routes: <RouteBase>[
GoRoute(
path: '/',
name: 'home',
builder: (context, state) => const HomePage(),
routes: <RouteBase>[
GoRoute(
path: 'detail/:id', // 完整路径:/detail/:id
name: 'detail',
builder: (context, state) {
final id = state.pathParameters['id']; // 路径参数
final msg = state.uri.queryParameters['msg']; // 查询参数
final extra = state.extra as String?; // 额外对象
return DetailPage(id: id ?? '0', msg: msg, extra: extra);
},
),
],
),
],
errorBuilder: (context, state) => ErrorPage(error: state.error),
);
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router, // 新写法(旧版可用 routeInformationParser/Delegate)
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
);
}
}
3. 页面与导航(常用 API)
dart
// HomePage → 导航到详情
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () {
// 路径导航(带路径参数与查询参数)
context.go('/detail/100?msg=hello');
},
child: const Text('Go Detail (path + query)'),
),
ElevatedButton(
onPressed: () {
// 命名导航 + 参数 + extra
context.pushNamed(
'detail',
pathParameters: {'id': '200'},
queryParameters: {'msg': 'from-named'},
extra: 'EXTRA_PAYLOAD',
);
},
child: const Text('Push Detail (named + extra)'),
),
],
),
),
);
}
}
class DetailPage extends StatelessWidget {
final String id;
final String? msg;
final String? extra;
const DetailPage({super.key, required this.id, this.msg, this.extra});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Detail #$id')),
body: Center(child: Text('msg=$msg, extra=$extra')),
);
}
}
context.go():替换式导航(相当于 Android 的 replace/clearTop)。context.push():入栈导航(类似 startActivity 入栈)。context.pop():返回。
4. 参数传递(路径/查询/extra)
- 路径参数:路由定义
path: 'detail/:id',读取state.pathParameters['id']。 - 查询参数:
/detail/1?msg=hi,读取state.uri.queryParameters['msg']。 - Extra:适合传对象(比如复杂实体),读取
state.extra并做类型断言。
5. 守卫与重定向(Auth、版本引导等)
最简示例(登录态守卫):
dart
class AuthState extends ChangeNotifier {
bool loggedIn = false;
}
final auth = AuthState();
final GoRouter router = GoRouter(
refreshListenable: auth, // 登录态变化时触发重定向
routes: [
GoRoute(path: '/', builder: (_, __) => const HomePage()),
GoRoute(path: '/login', builder: (_, __) => const LoginPage()),
GoRoute(path: '/profile', builder: (_, __) => const ProfilePage()),
],
redirect: (context, state) {
final loggingIn = state.matchedLocation == '/login';
if (!auth.loggedIn && !loggingIn) {
// 未登录访问受限页 → 重定向到 /login
return '/login';
}
if (auth.loggedIn && loggingIn) {
// 已登录不该再看到登录页 → 回首页
return '/';
}
return null; // 不重定向
},
);
要点:
refreshListenable可以监听状态变化(如登录态变更,自动触发路由刷新/重定向)。redirect返回String?,返回非空字符串表示重定向目的地。
6. 嵌套路由与 ShellRoute(底部导航)
dart
final router = GoRouter(
routes: [
ShellRoute(
builder: (context, state, child) {
return Scaffold(
body: child,
bottomNavigationBar: BottomNavigationBar(
currentIndex: _indexByLocation(state.matchedLocation),
onTap: (i) => _goTab(context, i),
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Me'),
],
),
);
},
routes: [
GoRoute(path: '/', builder: (_, __) => const HomePage()),
GoRoute(path: '/me', builder: (_, __) => const MePage()),
],
),
],
);
int _indexByLocation(String loc) => loc.startsWith('/me') ? 1 : 0;
void _goTab(BuildContext ctx, int i) {
if (i == 0) ctx.go('/'); else ctx.go('/me');
}
ShellRoute允许共享外层 UI(如 BottomNavigationBar),内部切换子路由。
7. 错误页与 404
dart
class ErrorPage extends StatelessWidget {
final Exception? error;
const ErrorPage({super.key, this.error});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Error')),
body: Center(child: Text('Oops: ${error?.toString() ?? 'Not Found'}')),
);
}
}
8. 深度链接(Deep Link)与 Web 支持
MaterialApp.router+GoRouter天然支持 Web URL。- 移动端可结合平台通道/第三方库接入 URI Scheme 或 App Links,然后
context.go()到目标路径。
9. 常见排错清单
- "页面不刷新/守卫不生效":确认
refreshListenable正确传入并在状态变化时调用notifyListeners()。 - "参数为空/拿不到":路径参数用
pathParameters,查询参数用state.uri.queryParameters,对象用extra。 - "返回行为异常":合理区分
go(替换)与push(入栈),栈结构与返回键行为会不同。 - "嵌套路由找不到页":确认父子
routes嵌套关系与path写法(子路径不要以斜杠起始时会拼接到父路径后)。
10. 最小模板(可直接复制)
dart
final router = GoRouter(
routes: [
GoRoute(path: '/', builder: (_, __) => const HomePage(), routes: [
GoRoute(path: 'detail/:id', builder: (_, s) => DetailPage(id: s.pathParameters['id'] ?? '0')),
]),
],
);
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: ElevatedButton(
onPressed: () => context.push('/detail/1?msg=hello'),
child: const Text('Go Detail'),
),
),
);
}