简介
路由负责实现应用程序页面之间的跳转。路由分为基本路由、命名路由和嵌模式三种。
1、基本路由
1.1、基础用法
- 调用
Navigator.push()方法。 - 使用
MaterialPageRoute创建路由。
说明:这是一种模态路由,可以适应平台自适应的过渡效果来切换页面。默认情况下当一个模态路由被另一个替换时,上一个路由将保存在内存中,如果详释放所有的资源,可以将
maintainState设置为false。
- 示例
// 跳转前页面的某个Widget点击方法中
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PersonSettingPage()),
);
},
在跳转后的页面,Scaffold组件会自动在AppBar上添加一个返回按钮,单击该按钮就会调用Navigator.pop方法回到跳转前页面。在自己添加的组件中调用该方法也可以回到跳转前页面。
- 示例
onPressed: () {
Navigator.pop(context);
},
1.2、PageRoute介绍
PageRoute是一个抽象类,它定义了页面导航行为和动画。Flutter 提供了几个常用子类:
MaterialPageRoute:Material Design 风格的默认页面切换(Android 风格)。CupertinoPageRoute:Cupertino 风格的页面切换(iOS 风格)。PageRouteBuilder:自定义路由构建器
上面的示例里面用到的就是MaterialPageRoute子类
1.3、MaterialPageRoute属性介绍
MaterialPageRoute 继承自 PageRoute → ModalRoute → TransitionRoute → OverlayRoute → Route,
builder:必选,builder是一个WidgetBuilder类型的函数,作用是构建路由页面的具体内用,返回值是Widget,即新的页面实例。setting:包含路由的配置信息,如路由名称(name)、传递的参数(arguments)。maintainState:默认为true,默认情况下,当新页面入栈时,原来的页面会被保存在内存中,如果想在页面没有用的时候释放其所占用的资源,只需将maintainState设置为false。fullscreenDialog:默认false,是否以一个全屏的模态对话框展示,在iOS中,若为true,则新页面会从屏幕底部向上划入。opaque:页面是否完全不透明,opaque在ModalRoute类中固定为true,这是因为MaterialPageRoute的设计目标是遵循 Material Design 规范,要求页面不透明。若想使页面可透明,可以使用其他路由类型,如PageRouteBuilder。barrierColor:全屏对话框背景遮罩颜色。这是一个只读属性,若想修改可以自定义类继承MaterialPageRoute,在自定义类中重写barrierColor的get方法。barrierDismissible:是否允许点击遮罩关闭对话框。
1.4、Navigator
Navigator是一个路由管理的组件,负责页面的跳转和层级管理。通过栈管理活动路由集合。通常屏幕当前实现的页面就是栈顶的页面。
方法
push:跳转到新页面,将页面压入栈顶部。pushNamed:通过预定义的命名路由跳转。pushReplacement:用新的页面替换当前页面,当前页面被销毁。pushReplacementNamed:通过命名路由替换当前路由。pushAndRemoveUntil:跳转到新路由,并清除堆栈中指定条件之前的所有路由。restorablePushNamed:支持状态恢复的命名路由跳转。pop:关闭当前页面,返回上一级路由,并可选传递返回值。popUntil:连续返回,直到满足指定条件。removeRoute:从栈中移除指定路由。replace:用新路由替换堆栈中的旧路由。canPop:检查当前是否可以执行返回操作。maybePop:尝试返回,如果无法返回则不执行。
2、命名路由
在 Flutter 中,命名路由(Named Routes) 是一种通过预定义路由名称来管理页面跳转的方式,可以简化导航逻辑并提高代码可维护性。
2.1、路由配置
在 MaterialApp 或 CupertinoApp 中定义路由表(routes):
MaterialApp(
// 初始路由(可选,与 home 二选一)
initialRoute: '/',
// 定义路由表
routes: {
'/': (context) => HomePage(),
'/details': (context) => DetailsPage(),
'/profile': (context) => ProfilePage(),
},
);
2.2、命名路由跳转
Navigator.pushNamed(context, "/newPage");
3、路由传值
在进行页面跳转时,通常还需要将一些数据传递给新页面,或者将数据返回给旧页面,这时就可以使用路由传值。
3.1、构造函数传值
顾名思义,在路由跳转的时候通过新页面的构造函数的参数传值给新页面。
3.2、路由setting参数传值
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LoginSuccessPage();
},
settings: RouteSettings(arguments: ["数据1", "数据2", "数据3"]),
),
);
// 在目标页面获取传递的数据
final List arguments = ModalRoute.of(context)?.settings.arguments as List;
3.3、命名路由传值
Navigator.pushNamed(
context,
'/details',
arguments: ["数据1", "数据2", "数据3"],
);
// 在目标页面获取传递的数据
final List arguments = ModalRoute.of(context)?.settings.arguments as List;
3.4、反向传值
pop函数的定义如下:
void pop<T>(BuildContext context, [T? result])
可以利用pop方法的可选参数result返回数据。
Navigator.pop(context, "返回的数据");
接收返回的数据
void onTap() async {
var result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LoginSuccessPage();
},
settings: RouteSettings(arguments: list),
),
);
if (result != null) {
print("result = $result");
}
}
4、路由拦截
通过在onGenerateRoute()方法中实现路由拦截,该方法的入参数据类型为RouteSettings,该类有两个变量,一个是Stirng?类型name,一个是Object?类型的arguments。name是定义的路由的名称,arguments则是想要传递的参数。
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
onGenerateRoute: (settings) {
// 拦截逻辑:若用户未登录且目标页面需要权限,跳转到登录页
if (settings.name == '/profile' && !isLoggedIn) {
return MaterialPageRoute(builder: (_) => LoginPage());
}
// 正常路由配置
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => HomePage());
case '/profile':
return MaterialPageRoute(builder: (_) => ProfilePage());
case '/login':
return MaterialPageRoute(builder: (_) => LoginPage());
default:
return MaterialPageRoute(builder: (_) => NotFoundPage());
}
},
);
}
上面说的是跳转的拦截,那返回的拦截即怎么实现呢?Flutter提供了PopScope组件,该组件可用于拦截返回按钮和安卓物理返回按钮的返回操作。通过它,我们可以实现在返回前执行特定的操作(如弹窗提醒,数据保存等)。
使用示例
class UsePopScopePage extends StatelessWidget {
const UsePopScopePage({super.key});
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, value) async {
if (didPop) return; // 已关闭则不再处理
final bool makeSure = await showDialog(
context: context,
builder:
(context) => AlertDialog(
title: Text("您确定要退出吗?"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text("确定"),
),
TextButton(
onPressed: () => Navigator.pop(context, false),
child: Text("取消"),
),
],
),
);
/* showDialog返回值为true,且context有效情况下调用pop继续返回,
会再次进入此方法,但此时didPop为true,直接返回。*/
if (makeSure && context.mounted) {
Navigator.pop(context);
}
},
child: Scaffold(
appBar: AppBar(title: Text("返回拦截")),
body: Center(child: Text("这里是返回拦截演示")),
),
);
}
}
参数介绍
canPop:实际开发中可能会根据全局状态或者局部状态来赋值,需注意,若想保留iOS、Android侧滑返回功能,需设为true。若设为true,则onPopInvokedWithResult方法中的didPop为true。若为false,则didPop为false,此时可以添加额外操作以决定是否继续返回。
onPopInvokedWithResult:此方法类型定义为:void Function(bool didPop, T? result)
didPop:bool类型,表示路由是否已经弹出。result:可空泛型参数,表示导航返回时开发者想要回传的数据。