什么是路由
在计算机领域,**路由(Route)**最早来源于 “路径/导航” 的概念:
- 网络里:路由器(Router)帮数据包找到路径。
- Web 开发里:URL 与页面之间的对应关系叫路由。
- Flutter 里:路由就是 一个页面(Screen/Page)的抽象,以及 页面之间跳转的规则。
在Flutter里面,路由的基本概念
Route(路由)
- 表示一个页面(Screen/Page)。
- 在 Flutter 里,页面通常就是一个
Widget
,比如一个Scaffold
,但必须由Route
来管理。
Navigator(导航器)
- 一个路由管理器,维护一个 栈结构(Stack)。
- 入栈(push)= 打开新页面。
- 出栈(pop)= 返回上一个页面。
MaterialApp
提供路由表(
routes
)、初始路由(initialRoute
)、未知路由处理(onUnknownRoute
)等。
匿名路由
一些概念
匿名路由主要是通过 Push()
Pop()
来操作路由,简单场景也能满足业务

Navigator
是一个路由管理的组件,它提供了打开和退出路由页方。
Future push(BuildContext context, Route route)
压入一个新页面到路由堆栈
bool pop(BuildContext context, [ result ])
压出一个页面出堆栈
MaterialPageRoute
继承自PageRoute
类,PageRoute
类是一个抽象类,表示占有整个屏幕空间的一个模态路由页面,它还定义了路由构建及切换时过渡动画的相关接口及属性。
MaterialPageRoute({
// 是一个WidgetBuilder类型的回调函数,它的作用是构建路由页面的具体内容,返回值是一个widget。
// 我们通常要实现此回调,返回新路由的实例。
WidgetBuilder builder,
// 包含路由的配置信息,如路由名称、是否初始路由(首页)。
RouteSettings settings,
// 默认情况下,当入栈一个新路由时,原来的路由仍然会被保存在内存中,
// 如果想在路由没用的时候释放其所占用的所有资源,可以设置maintainState为 false。
bool maintainState = true,
// 表示新的路由页面是否是一个全屏的模态对话框,
// 在 iOS 中,如果fullscreenDialog为true,新页面将会从屏幕底部滑入(而不是水平方向)。
bool fullscreenDialog = false,
})
路由传值
- 传递可以在初始新界面对象时通过构造函数压入
- 新界面退出后的返回值通过
Navigator.pop
的参数返回
代码
import 'package:flutter/material.dart';
import 'package:flutter_quickstart_learn/RouterPage.dart';
void main(List<String> args) {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', home: const NavPaged());
}
}
import 'package:flutter/material.dart';
class NavPaged extends StatelessWidget {
const NavPaged({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 页面标题
appBar: AppBar(title: const Text('NavPaged')),
body: Column(
children: [
Center(
//ElevatedButton : 按钮
child: ElevatedButton(
// onPressed: 按钮点击事件, 异步跳转到详情页
onPressed: () async {
// Navigator.push: 异步跳转到新页面
// 但是因为使用了 await,所以会等待新页面的返回值
var result = await Navigator.push(
context,
// MaterialPageRoute: 创建一个新的路由
MaterialPageRoute(
builder: (context) {
// DetailPaged: 详情页
return const DetailPaged(title: "create a new route");
},
),
);
// 上面会等待页面返回新的值,打印返回值
print("路由返回值: $result");
},
child: const Text("Navigator.push DetailPage"),
),
),
],
),
);
}
}
class DetailPaged extends StatelessWidget {
const DetailPaged({super.key, this.title});
// 参数
final String? title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('DetailPaged')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// 按钮
OutlinedButton(
// onPressed: 按钮点击事件, 异步返回到上一个页面
onPressed: () {
// Navigator.pop: 异步返回到上一个页面
// 为什么是异步的?因为Navigator.pop需要等待页面的销毁过程完成
// static void pop<T extends Object?>(BuildContext context, [T? result])
Navigator.pop(context, "ok");
},
child: const Text('Back'),
),
// 显示传值
Text(title ?? ""),
],
),
),
);
}
}


User NavPaged Navigator DetailPaged
| | | |
| 点击按钮 | | |
|--------------->| | |
| | Navigator.push | |
| |-------------------->| |
| | 创建路由对象 | |
| | |------ 创建并显示 ----->|
| | | |
| | <---- 返回 Future ---| |
| | (await 等待结果) | |
| | | |
| | | |
| | | 用户点击Back按钮 |
| | |<----------------------|
| | | Navigator.pop("ok") |
| | |---------------------->|
| | | 销毁 DetailPaged |
| | | |
| | <----- Future 完成 --| |
| | result = "ok" | |
| | 打印 "路由返回值:ok" | |
| | | |
命名路由
如果在routes里面定义了'/'
,则不能使用home
属性,否则会报错
If the home property is specified, the routes table cannot include an entry for "/", since it would be redundant.
意思是: 如果你在MaterialApp
中指定了home
,那么路由表 (routes
) 里就不能再写'/'
这个路由键,否则会冲突。
Navigator.pushNamed
: 异步跳转到详情页
Navigator.pop
: 返回到上一个页面
void main(List<String> args) {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
// 在这里定义路由的名字
// 注册路由表
routes: {
'/': (context) => const NavPaged(),
'/details': (context) => const DetailPaged(),
},
// 如果在routes里面定义了'/',则不能使用home属性,否则会报错
// home: const NavPaged(),
);
}
}
import 'package:flutter/material.dart';
class NavPaged extends StatelessWidget {
const NavPaged({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 页面标题
appBar: AppBar(title: const Text('NavPaged')),
body: Column(
children: [
Center(
//ElevatedButton : 按钮
child: ElevatedButton(
// onPressed: 按钮点击事件, 跳转到详情页
onPressed: () async {
// Navigator .pushNamed: 跳转到新页面
// 这里使用了命名路由,跳转到 DetailPaged 页面
/* @optionalTypeArgs
static Future<T?> pushNamed<T extends Object?>(
BuildContext context,
String routeName, {
Object? arguments,
})*/
var result = await Navigator.pushNamed(
context,
// 这里是 /details ,是命名路由的名称
"/details",
// 传递参数到 DetailPaged 页面
arguments: {'title': "create a new route"},
);
// 上面会等待页面返回新的值,打印返回值
print("路由返回值: $result");
},
child: const Text("Navigator.push DetailPage"),
),
),
],
),
);
}
}
class DetailPaged extends StatelessWidget {
// const DetailPaged({super.key, this.title});
const DetailPaged({super.key});
// 参数
// final String? title;
@override
Widget build(BuildContext context) {
// ModalRoute 是 Flutter 中一个类,表示当前页面的路由对象。
// ModalRoute.of(context) 会根据 context 找到当前页面对应的路由。
// ModalRoute.of(context)? : 里面的?表示如果找不到对应的路由,则返回null
// settings: 获取路由的设置
// settings.arguments: 获取路由传递的参数
// <String, dynamic>{}: 如果没有传递参数,则使用一个空的Map作为默认值
// as Map: 将获取到的参数强制转换为Map类型
final arguments =
(ModalRoute.of(context)?.settings.arguments ?? <String, dynamic>{})
as Map;
var title = arguments['title'];
return Scaffold(
appBar: AppBar(title: const Text('DetailPaged')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// 按钮
OutlinedButton(
// onPressed: 按钮点击事件, 返回到上一个页面
onPressed: () {
// Navigator.pop: 返回到上一个页面
// static void pop<T extends Object?>(BuildContext context, [T? result])
Navigator.pop(context, "ok");
},
child: const Text('Back'),
),
// 显示传值
Text(title ?? ""),
],
),
),
);
}
}
onGenerateRoute 手动解析
onGenerateRoute
的原理
- 作用:当
Navigator.pushNamed()
被调用时,Flutter 会先在routes
查找对应的路由;如果没找到,就会调用onGenerateRoute
。 - 好处:可以统一管理所有路由逻辑,包括参数传递、异常处理。
onGenerateRoute
更灵活,可以处理:
- 参数传递
- 未知路由(404 页面)
- 动态路由逻辑(比如权限判断)
一般推荐大项目用 onGenerateRoute
,因为它集中管理路由逻辑,更好维护。
import 'package:flutter/material.dart';
import 'package:flutter_quickstart_learn/RouterPage.dart';
void main(List<String> args) {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
// onGenerateRoute 是路由生成的回调
onGenerateRoute: (settings) {
// settings 是 RouteSettings 对象,里面包含了路由的名称和参数
print("settings.name: ${settings.name}");
// Handle '/'
if (settings.name == '/') {
return MaterialPageRoute(builder: (context) => const NavPaged());
}
// Handle '/details/:id'
var uri = Uri.parse(settings.name!);
if (uri.pathSegments.length == 2 &&
uri.pathSegments.first == 'details') {
String uid = uri.pathSegments[1];
return MaterialPageRoute(builder: (context) => DetailPaged(uid: uid));
}
return MaterialPageRoute(builder: (context) => const UnknownPage());
},
);
}
}
import 'package:flutter/material.dart';
class NavPaged extends StatelessWidget {
const NavPaged({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 页面标题
appBar: AppBar(title: const Text('NavPaged')),
body: Column(
children: [
Center(
//ElevatedButton : 按钮
child: ElevatedButton(
// onPressed: 按钮点击事件, 异步跳转到详情页
onPressed: () async {
// Navigator .pushNamed: 异步跳转到新页面
// 这里使用了命名路由,跳转到 DetailPaged 页面
/* @optionalTypeArgs
static Future<T?> pushNamed<T extends Object?>(
BuildContext context,
String routeName, {
Object? arguments,
})*/
var result = await Navigator.pushNamed(
context,
// 这里是 /details ,是命名路由的名称
"/details/312312312312",
// 传递参数到 DetailPaged 页面
arguments: {'title': "create a new route"},
);
// 上面会等待页面返回新的值,打印返回值
print("路由返回值: $result");
},
child: const Text("Navigator.push DetailPage"),
),
),
],
),
);
}
}
class DetailPaged extends StatelessWidget {
// const DetailPaged({super.key, this.title});
// const DetailPaged({super.key});
// 参数
// final String? title;
const DetailPaged({super.key, this.uid});
final String? uid;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('DetailPaged')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// 按钮
OutlinedButton(
// onPressed: 按钮点击事件, 异步返回到上一个页面
onPressed: () {
// Navigator.pop: 异步返回到上一个页面
// 为什么是异步的?因为Navigator.pop需要等待页面的销毁过程完成
// static void pop<T extends Object?>(BuildContext context, [T? result])
Navigator.pop(context, "ok");
},
child: const Text('Back'),
),
// 显示传值
Text(uid ?? ""),
],
),
),
);
}
}
class UnknownPage extends StatelessWidget {
const UnknownPage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(body: Text('UnknownPage'));
}
}
时序图:
sequenceDiagram participant U as User participant N as NavPaged participant Nav as Navigator participant App as MyApp.onGenerateRoute participant D as DetailPaged U->>N: 点击按钮 N->>Nav: pushNamed("/details/312312312312") Nav->>App: 调用 onGenerateRoute App-->>Nav: 返回 MaterialPageRoute Nav->>D: 加载并渲染 DetailPaged D-->>Nav: Navigator.pop("ok") Nav-->>N: 返回结果 "ok" N->>N: 打印返回值: ok
- User → NavPaged:用户点击按钮触发事件。
- NavPaged → Navigator:调用
pushNamed
。 - Navigator → MyApp.onGenerateRoute:根据路由规则生成页面。
- onGenerateRoute → Navigator:返回
MaterialPageRoute
。 - Navigator → DetailPaged:加载并渲染目标页面。
- DetailPaged → Navigator.pop(“ok”):用户返回时传回结果。
- Navigator → NavPaged:把
"ok"
返回给发起调用的地方。
routes
和onGenerateRoute
对比
特性 | routes (命名路由表) |
onGenerateRoute (动态路由生成) |
---|---|---|
定义方式 | 在 MaterialApp 的 routes 属性中写死一个 Map,{"/": (context) => HomePage(), "/detail": (context) => DetailPage()} |
在 onGenerateRoute 回调函数中,根据 settings.name 动态返回 Route |
初学者友好度 | ✅ 简单直观,适合小项目 | ❌ 相对复杂,需要写 switch 或逻辑判断 |
参数传递 | ⚠️ 不太方便,需要额外写构造函数或 settings.arguments 配合使用 |
✅ 非常方便,直接在 settings.arguments 里传,统一处理 |
扩展性 | ❌ 不灵活,路由必须预定义在 Map 中 | ✅ 高度灵活,可以做权限校验、动态跳转、日志记录 |
错误处理 | ❌ 如果路由未定义会报错,容易崩溃 | ✅ 可以处理未匹配路由,返回 404 页面 |
适用场景 | 小型应用,页面少,逻辑简单 | 中大型应用,需要统一管理、权限控制、参数传递复杂 |
可维护性 | 随着页面增加,routes 表越来越臃肿 |
所有路由逻辑集中在 onGenerateRoute ,更容易维护 |