开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情
路由(Route)在移动开发中指的是页面(Page),在iOS中通常为UIViewController,也就是单个显示页面。所以路由管理也就是页面跳转管理。iOS中通常用的是通过NavigationController 建立一个路由栈来管理。然后通过Push 和 Pop函数进行入栈和出栈。Flutter是通过Navigator来管理。
MaterialPageRoute 介绍
MaterialPageRoute继承自PageRoute类,PageRoute是一个抽象类,表示占有整个屏幕空间的一个模态路由页面,它还定义了路由构建及切换时过度动画的相关接口及属性。MateialPageRoute是Material组件库提供的组件,可以针对不同平台,实现与平台页面切换动画风格一致的路由切换动画。比如iOS的页面默认是从左到右进入,但是安卓是从底部弹出。
MaterialPageRoute 构造函数。
MaterialPageRoute({
WidgetBuilder builder,
RouteSettings settings,
bool maintainState = true,
bool fullscreenDialog = false,
})
- builder 是一个WidgetBuilder类型的回调函数,它的作用是构建路由页面的具体内容,返回一个widget。
- setting 包含路由配置信息,如名称,是否是初始路由(首页)。
- maintainState 默认情况下,当入栈一个新路由时,原来的路由仍会保存在内存中,如果想在路由没用的时候释放其所占用的所有资源,可以设置maintainState 为 false。
- fullcreenDialong 表示新的路由页面是否是一个全屏的模态对话框,在iOS中,如果fullscreenDialog为true,新页面将会从底部划入,而不是水平划入,类似于使用preseng VC。
Navigator
Navigator 是一个路由的管理组件,类似于iOS的NavigationgController,它提供的Push和Pop函数。
- Future push(BuildContext context, Route route),将传入的route(iOS 中的VC)进行入栈,返回一个Future对象,用于接收返回(出栈)时的返回数据。
- bool pop(BuildContext context, [result])将栈顶的页面出栈.
Navigator.push(BuildContext context, Route route)
等价于
Navigator.of(context).push(Route route)
- Navigator 还有很多其他的方法,比如Navigator.replace\Navigator.popUntil等。
路由传值
之前有介绍过页面之前的传值,是通过页面的构造函数,选择传递对应的数据。有传递,肯定就有返回,Navigator则含有一些可以在返回时传递的方法。下面是源码:
//新建一个Route
class TipRoute extends StatelessWidget{
const TipRoute({
Key? key,
required this.text,
}) : super(key: key);
final String text;
@override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: const Text("提示"),
),
body: Padding(
padding: const EdgeInsets.all(18),
child: Column(
children: <Widget>[
Text(text),
ElevatedButton(
onPressed: () => Navigator.pop(context, "一个返回值"),
//等价于onPressed: () {
// Navigator.pop(context, "一个返回值")
//},
child: const Text("返回"),
),
Image.asset('images/discover_games.png')
],
),
),
);
}
}
点击Push一个界面
var result = await Navigator.push(
context,
MaterialPageRoute(builder: (context){//注意:需要使用抽象类进行包装构建
return const TipRoute(text: "set a text",);
}),
).then((value){
print("current get back result $value");
});
// Navigator.pushNamed(context, "TipRoute", arguments: "hi");
//then 和 await 二者只会有一个回调
print("current get wait result $result");
这样就完成了单个页面的跳转数据接收和返回。
命名路由
命名路由有点类似于iOS中一些三方控件实现的一些路由管理,类似于通过特殊的协议进行跳转管理。Flutter中注意是通过一个map,进行管理,自定义路由key,value则是builder回调函数(为了生成对应的路由widget),应用会根据路由名称在路由表中进行查找,并将找到的路由通过回调生成widget并返回。大佬都推荐使用命名路由进行路由管理,方便后期跳转维护。
- 1、注册路由表: 回到之前的MaterialAPP中,可以找到一个routes属性。在属性中添加对应的route表示注册。
lass MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
onGenerateRoute: (RouteSettings setting){
return MaterialPageRoute(builder: (context){
return const TipRoute(text: "set a text",);
});
},
initialRoute: "/",//初始路由,也就是APP启动后首先加载的页面(首页)
routes: {
"NewRoute" : (context) => NewRoute(),
"TipRoute" : (context) => TipRoute(text: "sss"),
"/" : (context) => SSLTabbarCenterRoute(),//对应构建初始加载的页面
},
onGenerateInitialRoutes: null,
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
}
}
- 2、通过路由名打开新路由
常用的系统函数
Future pushNamed(BuildContext context, String routeName,{Object arguments})
//pushReplacementNamed
//源码实例
onPressed: (){
Navigator.pushNamed(context, "TipRoute", arguments: "hi");
//TipRoute为上面在路由表中注册的路由名称
//arguments 为传递参数,为可选对象,可接收任何任意类对象
}
- 3、通过路由获取参数 上面例子中可以看到通过命名路由的打开新的Route时,如果要传递参数,则通过arguments可选参数,在使用后可以通过RouteSetting对象获取参数
class TipRoute extends StatelessWidget{
const TipRoute({Key? key,required this.text,}) : super(key: key);
final String text;
@override
Widget build(BuildContext context){
var args = ModalRoute.of(context)?.settings.arguments;
return Scaffold();
}
}
通过上面的方式基本可以解决大多数场景的使用,但是还是有一些局限和不便,比如,命名路由的参数传递只有一个,如果多个参数可能需要包装数据,造成过多的逻辑冗余。这个后面会介绍一些其他的传值方式。
路由生成钩子
大家也许会有一些疑惑,如果是否可以有些页面先不注册,后面需要用的时候再注册行不行?答案是可以的。
大家可以看到MaterialApp中还有一个onGenerateRoute属性,在打开命名路由时,如果指定的路由名不在路由表中,则会回调此函数,否则不会回调,直接调用builder生成路由。源码实例:
title: 'Flutter',
onGenerateRoute: (RouteSettings setting){//在列表中未找到对应的命名路由
则返回一个新的命名路由
return MaterialPageRoute(builder: (context){
return const TipRoute(text: "set a text",);
});
},
initialRoute: "/",//首页路由
routes: {//路由列表
"NewRoute" : (context) => NewRoute(),
"TipRoute" : (context) => TipRoute(text: "sss"),
"/" : (context) => SSLTabbarCenterRoute(),//初始路由
},
- 需要注意的是MaterialApp中的三个属性(home、initialRoute和onGenerateRoute),在初始化时,必须满足其中一个,在没有initialRoute时,首页由home指定。