Flutter 中使用 go_router
1. 介绍与特性
在Flutter开发中,管理应用的导航逻辑是一项重要任务。默认情况下,Flutter提供了基本的Navigator
和Routes
系统来管理页面导航,但随着应用规模的增长,维护这些导航代码可能会变得复杂。为了解决这个问题,社区中有一些优秀的路由管理库,其中go_router
因其简单的API和强大的功能而广受欢迎。
本文将介绍如何在Flutter中使用go_router
来管理应用的路由。
特性:
- 使用模板语法解析路由路径和路由查询(query)参数;
- 支持单个目标路由展示多个页面(子路由);
- 重定向:可以基于应用状态跳转到不同的URL,比如用户没有登录时跳转到登录页;
- 使用 StatefulShellRoute 可以支持嵌套的 Tab 导航;
- 同时支持 Material 风格和 Cupertino 风格应用;
- 兼容 Navigator API 。
2. 安装go_router
首先,你需要在pubspec.yaml
文件中添加go_router
依赖:
dependencies:
flutter:
sdk: flutter
go_router: ^14.2.3
运行flutter pub get
以安装依赖。
3. 配置GoRouter
go_router
的核心是GoRouter
类,它用于定义和管理应用的路由。我们可以在主文件(如main.dart
)中初始化并配置GoRouter
。
3.1 基本路由配置
以下是一个简单的例子,展示了如何使用go_router
配置基本的路由:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() => runApp(const MyApp());
//路由配置
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
),
],
),
],
);
//MaterialApp.router 将 routerConfig 参数设置为 GoRouter 配置对象
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/details'),
child: const Text('Go to the Details screen'),
),
),
);
}
}
class DetailsScreen extends StatelessWidget {
const DetailsScreen({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details Screen')),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go back to the Home screen'),
),
),
);
}
}
在这个例子中,我们定义了两个路由:HomePage
和DetailsPage
。通过调用context.go('/details')
,我们可以从主页导航到详情页。
3.2 路由参数
有时,我们需要在路由之间传递参数。例如,在导航到详情页时,你可能想传递一个ID来显示特定的内容。我们可以通过在路径中添加参数来实现这一点:
final GoRouter _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomePage(),
),
GoRoute(
path: '/details/:id',
builder: (context, state) {
final id = state.params['id'];
return DetailsPage(id: id);
},
),
],
);
在DetailsPage
中,你可以通过构造函数接收传递的参数:
class DetailsPage extends StatelessWidget {
final String? id;
DetailsPage({required this.id});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Details')),
body: Center(
child: Text('Detail ID: $id'),
),
);
}
}
当你需要导航到这个页面时,可以像这样传递参数:
context.go('/details/123');
3.3 子路由
路由匹配后可以支持多个页面(即子路由)。当一个新的页面叠加在旧页面之上展示时,其效果与调用 push 方法类似。如果页面包含 AppBar 组件,返回按钮将自动添加。要使用子路由,只需在上级路由中添加相应的下级路由即可。
子路由: 是相对于父路由定义的路径,通常用于实现页面内的多个视图或功能。
final GoRouter _router = GoRouter(
routes: [
// 用户页面路由(包含子路由)
GoRoute(
path: '/user',
builder: (context, state) => UserScreen(),
routes: [
// 子路由1: 用户详情
GoRoute(
path: 'detail',
builder: (context, state) => UserDetailScreen(),
),
// 子路由2: 用户设置
GoRoute(
path: 'settings',
builder: (context, state) => UserSettingsScreen(),
),
],
),
],
);
// 导航到用户页面
context.go('/user');
// 导航到用户详情页面
context.go('/user/detail');
// 导航到用户设置页面
context.go('/user/settings');
3.4 嵌套路由
嵌套路由: 强调页面视图的层级结构,适用于复杂的页面布局和内容组织。
在复杂应用中,嵌套路由是常见的需求。go_router
支持嵌套路由配置:
下面示例中,MainScreen是顶层页面,ProfileScreen是MainScreen内部的嵌套页面,而EditProfileScreen是ProfileScreen内部的进一步嵌套页面。
final GoRouter _router = GoRoute(
path: '/',
builder: (context, state) => MainScreen(),
routes: [
GoRoute(
path: 'profile',
builder: (context, state) => ProfileScreen(),
routes: [
GoRoute(
path: 'edit',
builder: (context, state) => EditProfileScreen(),
),
],
),
],
)
在这种配置中,DetailsPage
仍然可以作为嵌套在HomePage
中的子页面访问。
3.4 重定向
有时,你可能需要根据某些条件进行重定向。go_router
允许你在路由配置中设置redirect
回调函数。例如,你可以根据用户的认证状态进行重定向:
final GoRouter _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomePage(),
),
GoRoute(
path: '/login',
builder: (context, state) => LoginPage(),
),
GoRoute(
path: '/dashboard',
builder: (context, state) => DashboardPage(),
),
],
redirect: (context, state) {
final bool loggedIn = isLoggedIn(); // 自定义的登录状态检查
final bool loggingIn = state.subloc == '/login';
if (!loggedIn && !loggingIn) return '/login';
if (loggedIn && loggingIn) return '/dashboard';
return null;
},
);
3.5 导航守卫
你还可以使用go_router
中的redirect
来实现导航守卫,这在控制用户访问权限时非常有用。例如,确保用户在访问某些页面之前已登录:
final GoRouter _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomePage(),
),
GoRoute(
path: '/login',
builder: (context, state) => LoginPage(),
),
GoRoute(
path: '/protected',
builder: (context, state) => ProtectedPage(),
redirect: (context, state) {
final loggedIn = isLoggedIn(); // 自定义的登录状态检查
return loggedIn ? null : '/login';
},
),
],
);
4. 结论
go_router
是一个功能强大且灵活的路由管理库,支持路由参数、嵌套路由、重定向和导航守卫等功能,使得在复杂应用中进行路由管理变得更加简洁和清晰。