1.6. 路由与导航
导航(Navigation)是移动应用的基本能力。Flutter 提供了灵活的路由系统。
1.6.1. 基本路由
1.6.2. 命名路由
在 MaterialApp 中预先注册路由:
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomePage(),
'/detail': (context) => const DetailPage(),
'/about': (context) => const AboutPage(),
},
)
// 使用命名路由导航
Navigator.pushNamed(context, '/detail');
Navigator.pushNamed(context, '/about');
// 返回上一页
Navigator.pop(context);
警告
命名路由不支持传递运行时参数(如动态 ID)。
如需传参,使用 onGenerateRoute 或 Navigator.push。
1.6.3. onGenerateRoute(动态路由)
更灵活的路由方案,支持参数传递:
MaterialApp(
onGenerateRoute: (settings) {
// 解析路由名称与参数
final uri = Uri.parse(settings.name ?? '');
final path = uri.path;
final queryParams = uri.queryParameters;
switch (path) {
case '/':
return MaterialPageRoute(
builder: (_) => const HomePage(),
);
case '/detail':
final id = int.tryParse(queryParams['id'] ?? '0') ?? 0;
return MaterialPageRoute(
builder: (_) => DetailPage(itemId: id),
);
default:
return MaterialPageRoute(
builder: (_) => const NotFoundPage(),
);
}
},
)
// 传递参数
Navigator.pushNamed(context, '/detail?id=42');
// 或
Navigator.pushNamed(context, '/detail', arguments: {'id': 42});
1.6.3.1. 命名路由中的参数提取
// 在目标页面提取参数
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
// 方式 1:从 RouteSettings 取
final args =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
final id = args['id'] as int;
// 方式 2:从路径解析
// final id = int.tryParse(
// ModalRoute.of(context)!.settings.name?.split('?').last ?? '0'
// ) ?? 0;
return Scaffold(
appBar: AppBar(title: Text('Detail $id')),
);
}
}
1.6.4. GoRouter(声明式路由)
GoRouter 是 Flutter 官方推荐的声明式路由方案,支持深度链接和重定向。
1.6.4.1. 添加依赖
pubspec.yaml
dependencies:
go_router: ^14.0.0
1.6.4.2. 基本用法
import 'package:go_router/go_router.dart';
// 定义路由配置
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
name: 'home',
builder: (context, state) => const HomePage(),
),
GoRoute(
path: '/detail/:id', // 路径参数
name: 'detail',
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailPage(itemId: int.parse(id));
},
routes: [
// 嵌套路由:/detail/:id/info
GoRoute(
path: 'info',
builder: (context, state) => const InfoPage(),
),
],
),
GoRoute(
path: '/search',
name: 'search',
builder: (context, state) => const SearchPage(),
),
],
);
// 在 MaterialApp.router 中使用
MaterialApp.router(
routerConfig: router,
title: 'My App',
);
1.6.4.3. 导航方法
// 跳转
context.go('/detail/42');
context.push('/detail/42');
context.pushNamed('detail', pathParameters: {'id': '42'});
// 返回
context.pop();
// 带查询参数
context.go('/search?q=flutter&page=1');
// 替换当前路由(不保留返回历史)
context.replace('/detail/99');
1.6.4.4. 重定向与守卫
final router = GoRouter(
redirect: (context, state) {
final loggedIn = _isLoggedIn;
final onLogin = state.matchedLocation == '/login';
// 未登录且不在登录页 → 跳转到登录
if (!loggedIn && !onLogin) return '/login';
// 已登录且在登录页 → 跳转到首页
if (loggedIn && onLogin) return '/';
// 不重定向
return null;
},
// ...
);
小技巧
GoRouter 的优势:
- 声明式:路由配置集中管理
- 路径参数:类似 Web 路由的 :id 语法
- 深度链接:自动支持 Android/iOS 的 URL Scheme
- 重定向:统一处理登录认证等守卫逻辑
- 嵌套路由:父子路由关系清晰
1.6.5. 路由方案选择
方案 |
适用场景 |
不推荐场景 |
|---|---|---|
Navigator.push/pop |
简单的 2-3 个页面跳转 |
页面多、需要深度链接 |
命名路由 |
页面固定且无需传参 |
需要动态参数或复杂路由 |
onGenerateRoute |
需要集中控制页面创建 |
嵌套路由需求复杂 |
GoRouter |
中大型应用、Web 端、深度链接 |
仅 1-2 页面的极简应用 |