Route —— 是 Flutter 中的一个抽象类,代表 Navigator 导航历史中的独立屏幕或页面。Route 的具体实现 —— MaterialPageRoute、CupertinoPageRoute 和 PlatformRoute —— 决定了屏幕如何显示以及过渡时使用什么动画。与普通 widget 不同,Route 拥有自己的生命周期,包含 didPush、didPop、didReplace 和 didChangeNext 方法。根据 Flutter API Reference (2026),每个 Route 管理 ModalBarrier,考虑平台特性(iOS 上的滑动返回),并确保屏幕间的状态隔离。
要点
Route —— 是 Flutter 导航系统的基础。用户在应用程序中看到的每个屏幕都由 Navigator 堆栈中的 Route 对象表示。Route 将屏幕从管理代码中抽象出来:Navigator 添加和删除 Route,而 Route 内部包含用户看到的界面。这种架构将导航和显示的职责分离开来。
与简单的 widget 替换不同,Route 提供普通 widget 无法使用的服务:ModalBarrier(对话框打开时背景变暗)、进出动画管理、Android 硬件 “返回” 按钮处理以及与 Hero 动画的集成,实现屏幕间的平滑过渡。
根据 Flutter Cookbook (2026),Route 是 Hero 动画的关键元素:一个 Route 上的 Hero widget 自动动画过渡到下一个 Route 上的 Hero widget,产生 “飞” 元素的效果。这正是因为 Route 在动画期间将两个屏幕都保留在 Overlay 中才得以实现。
Route 既是简单移动应用程序(通过 Navigator 1.0)的基础,也是复杂深度链接场景(Navigator 2.0)的基础。在 Navigator 2.0 中,Route 表示 Page,RouterDelegate 从路由配置中将其转换。因此,理解 Route 对于使用任何 Flutter 导航系统都是必需的 —— 无论选择哪种方法或包。
Flutter 提供了几种内置的 Route 实现,每种都将行为调整为特定平台。选择正确的 Route 类型会影响用户体验:Android 上的 Material 动画和 iOS 上的 Cupertino 动画营造出 “原生” 应用的感觉。
| Route 类型 | 动画 | 平台 | 特点 |
|---|---|---|---|
| MaterialPageRoute | 从下向上滑入 | Android、桌面端 | 过渡时阴影、自动 SafeArea 处理 |
| CupertinoPageRoute | 从右向左滑入 | iOS、iPadOS | 滑动返回手势、过渡时透明背景 |
| PlatformRoute | 自动选择 | 所有平台 | 根据 TargetPlatform 选择类型 |
| PageRouteBuilder | 自定义 | 所有平台 | 通过 AnimationController 完全控制动画 |
MaterialPageRoute —— 最常用的 Route 实现。它将新屏幕的进入从下到上进行动画,逐步出现。退出时,屏幕从上到下动画,返回初始位置。工具栏(AppBar)和屏幕主体分别动画,产生层次效果。
CupertinoPageRoute 模仿 iOS 的 UINavigationController。新屏幕从右侧进入,覆盖前一个屏幕。关键特点 —— 支持通过 CupertinoBackGestureDetector 实现的交互式滑动返回手势。即使在动画中间也能处理这个手势,为 iPhone 用户提供熟悉的自然行为。
Route 拥有自己独特的生命周期,与普通 StatefulWidget 的生命周期不同。理解这个周期对于正确初始化数据、订阅流以及在关闭屏幕时释放资源至关重要。
Route 的生命周期由四个主要阶段组成。Transition —— Route 创建并在进入时进行动画(调用 didPush)。Active —— Route 完全显示并与用户交互。Inactive —— 另一个 Route 覆盖当前 Route(对话框、底部屏幕),但 Route 仍保留在堆栈中。Disposed —— Route 从堆栈中移除并销毁,调用 didPop 和 dispose。
Route 的生命周期方法可以在自定义实现中重写。例如,didPop 在 Route 从堆栈中移除时调用 —— 这里可以保存数据草稿。didChangeNext 在堆栈中下一个 Route 发生变化时调用 —— 在导航历史更改时更新 UI 很有用。
根据 Flutter API Route.didPop (2026),请务必不要将 Route 的生命周期与 Route 内部 State 的生命周期混淆。Route 内部的 StatefulWidget 有自己的 initState 和 dispose,分别在 Transition 和 Disposed 阶段调用。Route 的寿命比其内部 State 更长 —— 即使其 widget 被另一个 Route 暂时隐藏,Route 仍保留在 Overlay 中。
Route 提供了在输入(创建时)和输出(完成时)传递数据的机制。通过 Route 正确传递数据消除了对全局变量和 InheritedWidget 的需求,使导航类型安全且可预测。
要将数据传递到新屏幕,使用接收 widget 的构造函数或 Navigator.pushNamed 中的 arguments 参数。在 Route 内部,数据可通过存储在 Route 对象中的 RouteSettings.arguments 访问。这种方法适用于所有 Route 类型 —— MaterialPageRoute、CupertinoPageRoute 和自定义实现。
要返回数据,使用 Navigator.pop(context, result) 的第二个参数。Navigator.push 返回一个 Future
在直接调用带有 MaterialPageRoute 的 Navigator.push 时,数据通过目标屏幕的构造函数传递。不使用 MethodChannel —— 这是纯 Dart 交互。这种方法更适合类型安全地传递复杂对象。
让我们来看一个创建带有自定义动画和数据传递的 自定义 Route 的示例。PageRouteBuilder 允许定义进入和退出动画,完全控制动画曲线和持续时间。
// 带滑动动画的自定义 Route
Navigator.push(context, PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return DetailPage(productId: '42');
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(0.0, 0.3);
const end = Offset.zero;
final tween = Tween(begin: begin, end: end);
final offsetAnimation = animation.drive(tween);
return SlideTransition(position: offsetAnimation, child: child);
},
transitionDuration: const Duration(milliseconds: 400),
));
// 从屏幕返回数据
ElevatedButton(
onPressed: () => Navigator.of(context).pop({'selected': true, 'id': '42'}),
child: const Text('选择'),
);
// 在调用屏幕上获取结果
final result = await Navigator.push(context, MaterialPageRoute(
builder: (context) => const SelectionPage(),
));
if (result != null) {
print('已选择:${result['selected']}');
}
在示例中,PageRouteBuilder 定义了一个带透明度的从底部滑入的自定义动画。transitionDuration 设置动画速度。代码还演示了结果的传递:详情屏幕返回一个包含用户选择的 Map,调用屏幕通过 push 返回的 Future 接收这些数据。Route 确保完全隔离:详情屏幕上的草稿不会影响列表的状态。
常见问题
Route —— 是在导航级别管理屏幕的对象:存储动画、ModalBarrier 和生命周期。Widget —— 是界面部分的描述。Route 内部包含 Widget,同时还提供普通 widget 无法使用的服务(Overlay 层、Hero 动画)。一个 Route 可以包含任意深度的复杂 widget 层次结构。
使用带有 pageBuilder(构建屏幕)和 transitionsBuilder(定义动画)参数的 PageRouteBuilder。在 transitionsBuilder 中,可以使用 animation(0.0–1.0)和用于并行动画的 secondaryAnimation。要完全控制,创建 Route 的子类并重写 buildPage、createAnimationController 和 buildTransitions,这将提供对低级 AnimationController 的访问。
要传递复杂对象,在直接 Navigator.push 时使用目标屏幕的构造函数,或在 pushNamed 时使用 arguments 参数。确保对象是可序列化的(Map、JSON 或自定义类)。在 Flutter 中,类型安全传递使用 freezed 或 json_serializable 模型,这些模型在通过 RouteSettings 传递时保证正确的反序列化。
如果 Route 保留在 Navigator 堆栈中,Route.dispose 不会被调用。例如,打开新 Route 时,旧 Route 进入非活动状态(Inactive),但不会被销毁 —— 它保留在堆栈中以供快速返回。只有通过 pop、pushReplacement 或 pushAndRemoveUntil 从堆栈中移除 Route 时,才会调用 Dispose。要释放资源,请使用 Route 内部 State 的 dispose,而不是 Route 本身的 dispose。
使用 ModalRoute.of(context) 从 BuildContext 获取当前 Route。ModalRoute.isActive 属性显示 Route 是否是当前可见的屏幕。ModalRoute.isCurrent —— 如果 Route 在堆栈顶部则为 true。要观察堆栈变化,通过 RouteAware 和 RouteObserver 订阅 Navigator 观察者,它们会在活动 Route 更改时发出通知。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。