Flutter 开发中实现页面跳转的核心知识点。Flutter 提供了两种导航方式:基础导航(适用于简单场景、少量页面) 和 命名路由(适用于复杂场景、多页面应用)
一、核心概念
在 Flutter 中,所有页面都是「Widget」,导航的本质是「管理页面栈(Stack)」:
- 「跳转页面」:将新页面「压入」栈顶(
push)。 - 「返回页面」:将当前页面「弹出」栈顶(
pop)。 - Flutter 提供了
Navigator组件来管理页面栈,日常开发中常用Navigator.of(context)来获取导航实例。
方案 1:基础导航(直接跳转,简单高效)
适用于 页面数量少(3-5 个)、无需传参 / 少量传参 的场景(如简单工具类 App),核心是 Navigator.push() 和 Navigator.pop()。
1.1 基本页面跳转(无参)
步骤 1:定义两个页面 Widget(首页 + 详情页)
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 导航示例',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(), // 首页
);
}
}
// 首页 Widget
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('首页')),
body: Center(
child: ElevatedButton(
onPressed: () {
// 核心:跳转至详情页(push 压入栈顶)
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailPage(), // 目标页面
),
);
},
child: const Text('跳转到详情页'),
),
),
);
}
}
// 详情页 Widget
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('详情页'),
// 导航栏默认自带返回按钮,点击等价于 Navigator.pop()
),
body: Center(
child: ElevatedButton(
onPressed: () {
// 核心:返回上一页(pop 弹出栈顶)
Navigator.pop(context);
},
child: const Text('返回首页'),
),
),
);
}
}
关键说明:
MaterialPageRoute:Material 风格的路由过渡动画(Android 上下滑动、iOS 左右滑动),无需手动配置。Navigator.push(context, route):将目标页面压入栈顶,实现跳转。Navigator.pop(context):将当前页面弹出栈顶,返回上一页(无返回值)。- 详情页的
AppBar会默认添加返回按钮,点击后自动调用Navigator.pop(),无需手动实现。
1.2 页面传参(带参跳转 + 返回值传递)
实际开发中常需要给下一页传参(如传递商品 ID),或接收下一页的返回值(如选择图片后返回图片路径)。
步骤 1:带参跳转(首页 → 详情页,传递字符串 / 数字)
修改 DetailPage 增加构造函数接收参数,修改首页的跳转逻辑:
// 详情页(修改:添加构造函数接收参数)
class DetailPage extends StatelessWidget {
final String title; // 接收的标题参数
final int id; // 接收的ID参数
// 构造函数(必填参数,也可设置可选参数)
const DetailPage({super.key, required this.title, required this.id});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('接收的ID:$id'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 返回上一页,并携带返回值("从详情页返回的内容")
Navigator.pop(context, "从详情页返回的内容");
},
child: const Text('返回首页并传值'),
),
],
),
),
);
}
}
// 首页(修改:带参跳转 + 接收返回值)
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('首页')),
body: Center(
child: ElevatedButton(
onPressed: () async {
// 异步接收返回值(await 等待 Navigator.push 执行完成)
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailPage(
title: '详情页(带参)',
id: 1001,
),
),
);
// 打印返回值(详情页传递的内容)
if (result != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('接收的返回值:$result')),
);
}
},
child: const Text('带参跳转到详情页'),
),
),
);
}
}
关键说明:
- 给下一页传参:通过目标页面的「构造函数」传递,支持所有 Dart 数据类型(字符串、数字、对象等)。
- 接收下一页返回值:
Navigator.push()是异步操作,返回Future,通过await可接收目标页面pop时传递的参数。 Navigator.pop(context, result):第二个参数是返回值,可省略(无返回值时)。
方案 2:命名路由(配置式跳转,适合复杂应用)
适用于 页面数量多(5 个以上)、需要统一管理、频繁跳转 的场景(如电商、社交 App),核心是「提前配置路由表,通过路由名称跳转」。
2.1 基本配置与跳转(无参)
步骤 1:在 MaterialApp 中配置 routes 路由表
路由表是一个 Map<String, WidgetBuilder>,key 是路由名称(通常以 / 开头),value 是页面构建函数。
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 命名路由示例',
theme: ThemeData(primarySwatch: Colors.blue),
// 1. 配置路由表
routes: {
'/': (context) => const HomePage(), // 首页(默认路由)
'/detail': (context) => const DetailPage(), // 详情页
},
// 2. 设置默认启动页面(等价于 home: const HomePage())
initialRoute: '/',
);
}
}
步骤 2:通过路由名称跳转 / 返回
修改首页的跳转逻辑,使用 Navigator.pushNamed() 替代 Navigator.push():
// 首页
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('首页')),
body: Center(
child: ElevatedButton(
onPressed: () {
// 核心:通过路由名称跳转
Navigator.pushNamed(context, '/detail');
},
child: const Text('命名路由跳转到详情页'),
),
),
);
}
}
关键说明:
routes:路由表,统一管理所有页面,便于后期维护(修改页面只需修改路由表,无需修改所有跳转处)。initialRoute:默认启动路由(替代home),必须和路由表中的某个 key 对应。Navigator.pushNamed(context, '/detail'):通过路由名称跳转,无需再创建MaterialPageRoute。- 返回逻辑不变,依然使用
Navigator.pop(context)。
2.2 命名路由传参(带参跳转)
命名路由传参有两种常用方式:arguments 传参(推荐)、路由名称拼接参数(不推荐,仅适用于简单参数)。
方式 1:arguments 传参(推荐,支持任意数据类型)
- 跳转时通过
arguments传递参数:
// 首页跳转逻辑修改
ElevatedButton(
onPressed: () {
// 传递参数(可传字符串、数字、Map、自定义对象等)
Navigator.pushNamed(
context,
'/detail',
arguments: {
'title': '详情页(命名路由传参)',
'id': 1001,
},
);
},
child: const Text('命名路由带参跳转'),
),
- 详情页通过
ModalRoute.of(context)?.settings.arguments接收参数:
// 详情页修改
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
// 接收 arguments 参数
final Map<String, dynamic> args = ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>;
return Scaffold(
appBar: AppBar(title: Text(args['title'])),
body: Center(
child: Text('接收的ID:${args['id']}'),
),
);
}
}
方式 2:路由名称拼接参数(仅适用于简单参数,如 ID)
// 跳转时拼接参数
Navigator.pushNamed(context, '/detail/1001');
// 配置路由表时使用通配符
routes: {
'/detail/:id': (context) => const DetailPage(),
},
// 详情页提取参数(需额外解析,较繁琐,不推荐)
final String id = ModalRoute.of(context)?.settings.name?.split('/').last ?? '';
2.3 未知路由处理(404 页面)
当跳转的路由名称不在路由表中时,会出现错误,可通过 onUnknownRoute 配置兜底页面(404 页面):
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 命名路由示例',
theme: ThemeData(primarySwatch: Colors.blue),
routes: {
'/': (context) => const HomePage(),
'/detail': (context) => const DetailPage(),
},
initialRoute: '/',
// 未知路由兜底
onUnknownRoute: (settings) {
return MaterialPageRoute(
builder: (context) => const NotFoundPage(),
);
},
);
}
}
// 404 页面
class NotFoundPage extends StatelessWidget {
const NotFoundPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('页面不存在')),
body: const Center(child: Text('404 - 页面未找到')),
);
}
}
方案 3:进阶:路由管理(封装路由工具类)
当项目页面非常多时,直接在 MaterialApp 中配置路由表会显得臃肿,推荐封装「路由工具类」,统一管理路由跳转、传参、拦截等逻辑。
3.1 封装路由工具类
import 'package:flutter/material.dart';
import 'package:your_project_name/pages/home_page.dart';
import 'package:your_project_name/pages/detail_page.dart';
import 'package:your_project_name/pages/not_found_page.dart';
// 路由名称常量(避免硬编码,便于维护)
class RouteNames {
static const String home = '/';
static const String detail = '/detail';
}
// 路由工具类
class RouteManager {
// 配置路由表
static Map<String, WidgetBuilder> routes = {
RouteNames.home: (context) => const HomePage(),
RouteNames.detail: (context) => const DetailPage(),
};
// 路由跳转(封装 pushNamed,统一处理传参)
static Future<T?> pushNamed<T>(BuildContext context, String routeName, {Object? arguments}) {
return Navigator.pushNamed<T>(context, routeName, arguments: arguments);
}
// 返回上一页(封装 pop,统一处理返回值)
static void pop<T>(BuildContext context, {T? result}) {
Navigator.pop<T>(context, result);
}
// 未知路由处理
static Route<dynamic> onUnknownRoute(RouteSettings settings) {
return MaterialPageRoute(
builder: (context) => const NotFoundPage(),
);
}
}
3.2 使用封装后的路由工具类
// MyApp 中配置
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 路由封装示例',
theme: ThemeData(primarySwatch: Colors.blue),
routes: RouteManager.routes,
initialRoute: RouteNames.home,
onUnknownRoute: RouteManager.onUnknownRoute,
);
}
}
// 首页跳转
ElevatedButton(
onPressed: () {
RouteManager.pushNamed(
context,
RouteNames.detail,
arguments: {'title': '详情页(封装路由)', 'id': 1001},
);
},
child: const Text('封装路由带参跳转'),
),
// 详情页返回
ElevatedButton(
onPressed: () {
RouteManager.pop(context, result: "从封装路由详情页返回");
},
child: const Text('返回首页'),
),
关键补充:路由过渡动画自定义
默认的 MaterialPageRoute 是 Material 风格动画,若需要自定义过渡动画(如淡入淡出、缩放),可使用 PageRouteBuilder:
// 自定义淡入淡出动画跳转
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const DetailPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
// 淡入淡出动画
return FadeTransition(
opacity: animation,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 500), // 动画时长
),
);
总结
- Flutter 导航核心是「页面栈管理」,通过
Navigator实现push(跳转)和pop(返回)。 - 简单场景用「基础导航」(
Navigator.push),快速高效;复杂场景用「命名路由」(Navigator.pushNamed),便于统一管理。 - 传参方式:基础导航用「构造函数」,命名路由用「
arguments」(推荐)。 - 最佳实践:页面较多时封装「路由工具类」,统一管理路由表、跳转、未知路由,提升项目可维护性。
- 自定义过渡动画可使用
PageRouteBuilder,满足个性化 UI 需求。