🪶序言
🧩什么是路由?
通俗地说:
路由(Route) 是页面跳转的规则和路径管理机制。
在任何应用中,你都会从一个页面跳到另一个页面,例如:
- 登录页 → 首页
- 首页 → 详情页
- 设置页 → 关于页
系统需要知道:
"当我跳转到
/home
时,应该显示哪个页面?""当我从
/detail?id=10
返回时,应该回到哪个页面?"
这个"路径与页面对应关系"的管理系统,就是 路由系统(Routing System);它负责维护页面栈、管理页面入栈和出栈。
而如何使用和控制这个系统,让用户能够在应用中顺畅地跳转、传递参数、做权限检查,就是 路由管理(Route Management)。
举例:
术语 对应关系 路由系统 公交系统:提供车辆、线路和站点等基础设施 路由管理 调度员/司机:控制什么时候发车、走哪条路线、终点在哪
📖什么是路由栈?
路由栈(Route Stack )指的是 页面访问顺序形成的一种 栈式结构
,类似数据结构中的栈(Stack:后进先出 LIFO)。
🔹 基本原理
- 每次打开新页面,页面会 入栈(push) 。
- 每次返回上一页,页面会 出栈(pop) 。
- 栈顶的页面永远是当前显示的页面。
🔹 举例说明
假设应用有三个页面:首页
、详情页
、设置页
。操作顺序如下:
- 打开首页 → 栈中:
[首页]
- 首页跳转到详情页 → 栈中:
[首页, 详情页]
- 详情页跳转到设置页 → 栈中:
[首页, 详情页, 设置页]
- 设置页点击返回 → 栈中:
[首页, 详情页]
,显示详情页
💡 栈顶的页面永远是当前显示的页面,返回操作就是出栈。
🔹 路由栈的作用
- 管理页面顺序:确保用户按访问顺序返回。
- 控制页面状态:栈中的页面保持状态,不会被销毁(除非手动移除)。
- 方便跳转操作:可以实现"替换页面"或"清空栈跳转新页面"等复杂导航需求。
📚Flutter 中的路由机制
在 Flutter 中,每个 页面(Screen) 都被看作一个 Route(路由)对象。
- Flutter 默认使用
Navigator
来管理路由栈(Route Stack)。 - 路由栈的概念类似浏览器的"前进"和"后退"按钮,
Navigator
控制页面的入栈(push)和出栈(pop)。
🔹 常见操作
操作 | 方法 | 含义 |
---|---|---|
打开页面 | Navigator.push() |
入栈一个新页面 |
返回页面 | Navigator.pop() |
出栈当前页面 |
替换页面 | Navigator.pushReplacement() |
用新页面替换当前页面 |
清空栈跳转 | Navigator.pushNamedAndRemoveUntil() |
清空历史页面栈后跳转新页面(如登录成功后) |
🔹 示例:原生路由方式
dart
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailPage(id: 10)),
);
🔹 示例:使用命名路由
在 MaterialApp
中注册路由:
dart
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomePage(),
'/detail': (context) => DetailPage(),
},
);
跳转页面:
dart
Navigator.pushNamed(context, '/detail');
⚠️ 注意:虽然原生路由功能强大,但在大型项目中使用
Navigator
管理命名路由和参数传递可能会比较繁琐,需要手动传context
并处理返回值、参数和栈管理。
GetX - 路由管理(Route Management)
1. 基本概念
在 Flutter 中使用 GetX 可以大幅简化路由管理。相比原生 Navigator
,具有如下特点:
- 无需 BuildContext:可全局访问路由,不依赖 widget 树。
- 简化栈操作:轻松控制页面入栈、替换或清空栈。
- 支持参数与返回值:传参和接收返回数据更直观。
- 中间件机制:可在路由跳转前后添加逻辑,如权限检查或登录拦截。
- 叠加路由统一管理:Snackbar、Dialog、BottomSheet 等覆盖页面属于叠加路由,它们不在 Navigator 栈中,而是通过 Overlay 层管理的临时 UI。
💡 小结 :
GetX 将原生
Navigator
的繁琐操作封装起来,使路由管理更加直观、高效,适合大型项目的页面跳转和全局控制需求。
2. 路由使用
💬 注意入口文件更改
dartclass MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // return MaterialApp( 替换 return GetMaterialApp( title: 'GetX Demo', debugShowCheckedModeBanner: false, theme: ThemeData(primarySwatch: Colors.blue), home: HomePage(), // 首页 ); } }
①. 普通路由
普通路由 指的是应用中最基础的页面跳转方式,只负责在页面之间切换,而 不携带任何参数 或仅依赖页面自身构造函数初始化。
(1)Get.to()
- 作用:跳转到新页面,并将其入栈(Stack Push)。
- 使用场景:常规页面跳转,如首页 → 详情页。
- 原生对标 :
Navigator.push()
dart
Get.to(NextScreen());
(2)Get.off()
- 作用:用新页面替换当前页面(Stack Replace Top)。
- 使用场景:登录成功后替换登录页、完成表单后跳转结果页。
- 原生对标 :
Navigator.pushReplacement()
dart
Get.off(NextScreen());
(3)Get.offAll()
- 作用:清空整个页面栈,并跳转到新页面(Clear Stack + Push)。
- 使用场景:退出登录后返回首页、重置应用导航。
- 原生对标 :
Navigator.pushAndRemoveUntil()
dart
Get.offAll(NextScreen());
(4)Get.back()
- 作用:返回上一页,将当前页面出栈(Stack Pop)。
- 使用场景:用户点击返回按钮、完成操作后回退。
- 原生对标 :
Navigator.pop()
dart
Get.back();
(5)await Get.to()
- 作用:跳转到新页面,并在页面关闭时获取返回值。
- 使用场景:用户在表单页、选择页、弹窗页操作完成后返回结果给上一个页面。
- 原生对标 :
var result = await Navigator.push(...)
dart
// 跳转并等待返回值
var data = await Get.to(NextScreen());
// 在 NextScreen 中返回数据
Get.back(result: 'Hello World');
// 返回后 data = 'Hello World'
②. 命名路由
(1)概念
命名路由 是为每个页面指定一个 唯一的字符串标识(路由名) ,通过这个名字来进行页面跳转,而不是直接使用 页面类或 Widget。
(2)命名路由实现

1.集中定义路由名
dartclass Routes { static const splash = '/splash'; static const login = '/login'; static const bottomNav = '/bottomNav'; static const train = '/train'; }
2.在
GetPage
中注册路由
dartfinal pages = [ GetPage(name: Routes.splash, page: () => const SplashPage(), binding: SplashBinding()), GetPage(name: Routes.login, page: () => const LoginPage(), binding: LoginBinding()), GetPage(name: Routes.bottomNav, page: () => BottomNavView(), binding: BottomNavBinding()), GetPage(name: Routes.train, page: () => const TrainPage(), binding: TrainBinding()), ];
3.通过路由名跳转
dart// 跳转到登录页 Get.toNamed(Routes.login); // 替换当前页 Get.offNamed(Routes.bottomNav); // 清空栈并跳转 Get.offAllNamed(Routes.train); // 返回上一页 Get.back();
(3)特点与优势
特点 | 说明 |
---|---|
集中管理 | 所有路由名统一在一个类里,维护方便 |
无需直接引用 Widget | 跳转只需要路由名,减少模块耦合 |
支持全局跳转 | 不需要 BuildContext ,可在任意位置调用 |
方便传参与中间件 | 可在跳转时传递参数,也可在路由注册时加中间件 |
(4)对标原生 Navigator
GetX 命名路由 | 原生 Navigator |
---|---|
Get.toNamed('/login') |
Navigator.pushNamed(context, '/login') |
无需 context |
需要 BuildContext |
支持中间件、绑定 | 需要手动封装逻辑 |
支持全局访问 | 受 widget 树约束 |
③. 传参路由
传参路由指在应用中从一个页面跳转到另一个页面时,同时携带数据(参数)的路由方式。
(1)Get.arguments 传参
- 使用方法 :通过
Get.arguments
传递任意对象或 Map。 - 示例:
dart
// 跳转并传参
Get.to(DetailPage(), arguments: {'id': 10, 'name': 'Flutter'});
// 目标页面获取参数
class DetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final args = Get.arguments; // {'id': 10, 'name': 'Flutter'}
return Scaffold(
body: Center(
child: Text('ID: ${args['id']}, Name: ${args['name']}'),
),
);
}
}
- 使用场景:跨模块传参、临时数据、无需修改页面构造函数。
- 原生对标:
dart
// 跳转并传参
Navigator.pushNamed(context, '/page', arguments: data);
// 目标页面获取参数
ModalRoute.of(context)!.settings.arguments;
(2)Get.parameters 传参
- 使用方法:通过命名路由的 URL 查询参数形式传递,参数会作为字符串存储。
- 示例:
dart
// 跳转并传递参数
Get.toNamed('/detail?id=10&name=Flutter');
// 目标页面获取参数
class DetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final params = Get.parameters; // {'id': '10', 'name': 'Flutter'}
return Scaffold(
body: Center(
child: Text('ID: ${params['id']}, Name: ${params['name']}'),
),
);
}
}
- 使用场景:跨模块、全局跳转、只需简单标识或筛选条件的场景。
- 原生对标:
dart
// 跳转并传递参数
Navigator.pushNamed(context, '/detail?id=10&name=Flutter');
// 原生 Flutter 需要自己解析 URL 才能拿到参数
final uri = Uri.parse(ModalRoute.of(context)!.settings.name!);
final params = uri.queryParameters; // {'id': '10', 'name': 'Flutter'}
⚠️ 注意:
Get.parameters
中的值都是 String 类型,如需其他类型需手动转换。
④. 中间件
(1)概念
中间件(Middleware) 在路由管理中指的是在页面跳转前或跳转后执行的一段 逻辑 ,它可以对路由行为 进行拦截、处理或增强,而不需要修改页面本身。
(2)主要作用
- 权限控制:如用户未登录时自动跳转到登录页。
- 路由拦截:根据条件阻止或修改跳转目标。
- 统一处理:日志记录、统计、参数检查、页面初始化等。
(3)使用中间件
1.创建中间件类:
dartclass AuthMiddleware extends GetMiddleware { @override RouteSettings? redirect(String? route) { return null; } }
2.在路由中绑定中间件:
dartGetPage( name: '/profile', page: () => ProfilePage(), middlewares: [AuthMiddleware()], ),
3.执行流程:
- 用户尝试访问
/profile
- 中间件的
redirect
会先被执行- 根据条件决定后续逻辑
3. 叠加路由
叠加路由(Overlay Route) 指在当前页面之上显示新的页面或 UI 层,而不替换或关闭底层页面。其主要特点:
- 底层页面保留状态:原页面仍可操作或返回。
- 适用于临时 UI:如弹窗、对话框、底部弹层、全屏浮层等。
- 支持多层叠加:可在已有浮层上继续叠加新的页面或组件。
① Snackbar
🔹 GetX VS 原生
dart
// GetX
Get.snackbar(
'标题',
'内容',
snackPosition: SnackPosition.BOTTOM,
duration: Duration(seconds: 2),
);
// Flutter 原生
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('内容'),
duration: Duration(seconds: 2),
),
);
② Dialog
🔹 GetX VS 原生
dart
// GetX
Get.dialog(
AlertDialog(
title: Text('提示'),
content: Text('这是一个对话框'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('确定'),
),
],
),
);
// Flutter 原生
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('提示'),
content: Text('这是一个对话框'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('确定'),
),
],
),
);
③. BottomSheet
🔹 GetX VS 原生
dart
// GetX
Get.bottomSheet(Container(...));
// Flutter 原生
showModalBottomSheet(
context: context,
builder: (_) => Container(...),
);
💡总结
维度 GetX Flutter 原生 优势 Context依赖 不依赖 依赖 逻辑层可直接调用,无需 context 代码简洁 一行即可 多行 builder
+context
开发效率高 全局调用 可在控制器/服务调用 通常受 Widget 树限制 全局消息、提示更方便 自定义 位置、动画、按钮、颜色等 需要手动封装 灵活度高 路由整合 叠加路由体系 独立实现 生命周期管理方便
④. Overlay Route 原理
Overlay Route(叠加路由) 的核心原理是在现有页面上插入一个 新的渲染层(OverlayEntry) ,而不销毁或替换底层页面。
核心机制:
- Overlay Widget
- Flutter 提供了
Overlay
Widget,它本质上是一个 可堆叠的层级容器。 - 所有的叠加 UI(如弹窗、Tooltip、Snackbar、BottomSheet)都通过
OverlayEntry
插入到 Overlay 中。
- Flutter 提供了
- OverlayEntry
- 每个叠加路由都会生成一个
OverlayEntry
。 OverlayEntry
是一个可插入 Overlay 树的独立 Widget,可以控制显示和移除。
- 每个叠加路由都会生成一个
- 渲染流程
- 当叠加路由触发时:
- 系统创建对应的
OverlayEntry
。 - 插入到 Overlay 层级(通常在最上层)。
- 显示内容(Dialog、Snackbar 等)。
- 系统创建对应的
- 移除时,
OverlayEntry.remove()
将其从 Overlay 树中移除,不影响底层页面。
- 当叠加路由触发时:
- 特点
- 不影响底层页面状态:底层 Widget 树保持完整。
- 支持多层叠加:可以在已有 OverlayEntry 上继续插入新的叠加层。
- 灵活控制显示与消失:可通过动画、定时、手势等控制 OverlayEntry 的生命周期。
4. 高级应用
①. 路由守卫
1️⃣ 创建守卫类
dart
import 'package:get/get.dart';
class AuthGuard extends GetMiddleware {
// 权重,数字越大越先执行
@override
int? priority = 0;
// 页面跳转前拦截
@override
RouteSettings? redirect(String? route) {
bool isLoggedIn = false; // 模拟登录状态,可换成实际逻辑
if (!isLoggedIn) {
// 如果未登录,跳转到登录页
return RouteSettings(name: '/login');
}
// 已登录,允许访问目标页面
return null;
}
}
2️⃣ 在 GetPage 中使用守卫
dart
GetPage(
name: '/home',
page: () => HomePage(),
middlewares: [AuthGuard()], // 添加守卫
),
GetPage(
name: '/login',
page: () => LoginPage(),
),
3️⃣ 页面跳转
dart
// 用户尝试访问首页
Get.toNamed('/home'); // 如果未登录,会被 AuthGuard 重定向到 /login
说明
GetMiddleware
可以在路由跳转前/后做拦截。redirect
方法用于在跳转前决定是否重定向到其他路由。priority
控制多个守卫执行顺序,数字越大优先级越高。
②. 自定义动画
在页面跳转时,你可以自定义过渡动画,使路由切换更加流畅且符合应用风格。
1️⃣ 使用内置动画
dart
Get.to(
DetailPage(),
transition: Transition.rightToLeft, // 内置动画类型
transitionDuration: Duration(milliseconds: 500), // 动画时长
curve: Curves.easeInOut, // 动画曲线
);
常用内置动画类型:
动画类型 | 描述 |
---|---|
fade |
渐隐渐现 |
fadeIn |
页面淡入 |
rightToLeft |
从右向左滑入 |
leftToRight |
从左向右滑入 |
upToDown |
从上向下滑入 |
downToUp |
从下向上滑入 |
rightToLeftWithFade |
右滑同时渐隐渐现 |
leftToRightWithFade |
左滑同时渐隐渐现 |
zoom |
缩放动画 |
zoomRotate |
缩放+旋转 |
size |
从零大小展开 |
circularReveal |
圆形揭示动画(类似水波纹展开) |
topLevel |
顶层覆盖动画(全屏显示) |
noTransition |
无过渡动画 |
cupertino |
Cupertino 风格 iOS 页面切换动画 |
cupertinoDialog |
Cupertino 风格对话框动画 |
native |
使用原生平台默认动画 |
展示:


2️⃣ 自定义动画 (CustomTransition
)
如果内置动画无法满足需求,可以自定义动画,通过 CustomTransition
完全控制动画效果。
dart
Get.to(
DetailPage(),
customTransition: MyCustomTransition(),
transitionDuration: Duration(milliseconds: 600),
);
自定义动画示例:
dart
class MyCustomTransition extends CustomTransition {
@override
Widget buildTransition(BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
// 这里使用 Fade + Slide 混合动画
return SlideTransition(
position: Tween<Offset>(
begin: Offset(1, 0), // 从右边进入
end: Offset.zero,
).animate(animation),
child: FadeTransition(
opacity: animation,
child: child,
),
);
}
}
展示:
说明:
animation
:主动画曲线,可控制方向、透明度、缩放等。secondaryAnimation
:二级动画,可在返回时使用。child
:目标页面组件。
通过自定义 CustomTransition
,你可以实现几乎任何动画效果,包括旋转、缩放、组合动画等。