Flutter 之Router 页面跳转
页面跳转在移动开发中是很常见的事情,在Android中打开另外一个页面主要是用startActivity这个方法,在Flutter中也是提供这种能力,主要的使用方式就是通过Navigator 去打开一个页面
1.跳转到另外一个页面
构建FirstScreen和SecondScreen 页面
import 'package:flutter/material.dart'; class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) { return SecondScreen(); })); }, child: Text("next screen"), ), ), ); } } class SecondScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Second Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pop(context); }, child: Text("back"), ), ), ); } }
这里就是跳转的主要代码
Navigator.push(context, MaterialPageRoute(builder: (context) { return SecondScreen(); }));
push方式详解
static Future<T> push<T extends Object>(BuildContext context, Route<T> route)
第一个参数就是上下问信息,类似Android中的Context,第二个参数就是路由信息,也就是要打开的主要页面是哪个,MaterialPageRoute 就是Route其中的一个子类,用于在Material Desgin 模式下打开页面的
Navigator.pop(context);
是用来返回上一个页面的
2.通过routes路径方式跳转到下一个页面
先定义Routes路由表,实际上就是一个Map结构,key是路径,value就是对应的页面
import 'package:flutter/material.dart'; import 'navigation/navigation_demo.dart'; void main() { runApp(MaterialApp( initialRoute: "/", routes: { "/": (context) => FirstScreen(), '/second': (context) => SecondScreen(), }, )); }
routes 就是一个map结构,根目录/对应的页面就是FirstScreen,/second路径对应的页面就是ScendScreen,在FirstScreen中打开SecondScreen的方式我们换一下,要通过Navigator.pushNamed方式打开一个在路由表中已经存在的页面
class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pushNamed(context, "/second"); // Navigator.push(context, MaterialPageRoute(builder: (context) { // return SecondScreen(); // })); }, child: Text("next screen"), ), ), ); } }
3.传递数据到下一个页面
传递数据到下一个页面也是比较常见的情况,例如说在一个相册应用中,有一个列表页面,单击列表中某一个item,应该跳转到照片的详情页面,其实这种情况就应该把照片的信息传递给另外一个页面
传递的方式有两种:
- 在构造方法中传递数据
- 在Route中传递数据给下一个页面
在第一个页面构造要传递的数据
class Photo { String title; String message; Photo({this.title, this.message}); } class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pushNamed(context, "/second", arguments: Photo(title: "pass title",message: "pass message")); // Navigator.push(context, MaterialPageRoute(builder: (context) { // return SecondScreen(); // })); }, child: Text("next screen"), ), ), ); } }
在第二个页面获取数据
class SecondScreen extends StatelessWidget { @override Widget build(BuildContext context) { final Photo photo=ModalRoute.of(context).settings.arguments; return Scaffold( appBar: AppBar( title: Text("Second Screen ${photo.title}"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pop(context); }, child: Text("back ${photo.message}"), ), ), ); } }
这种方式有种不太好的地方就是需要在下一个页面通过ModalRoute.of(context).settings.arguments; 方式获取传递的数据,其实Flutter中已经提供了这种方式简便处理方式
import 'package:flutter/material.dart'; import 'navigation/navigation_demo.dart'; void main() { runApp(MaterialApp( home: FirstScreen(), onGenerateRoute: (settings) { if (settings.name == ThreeScreen.routeName) { final Photo args = settings.arguments; return MaterialPageRoute(builder: (context) { return ThreeScreen( title: args.title, message: args.message, ); }); } }, )); }
onGenerateRoute 是用来统一拦截传递参数的方法,我们可以在这个地方获取传递的数据,然后在构造页面的时候把参数传递给目标页面,这样在目标页面也就是不用考虑如何解析传递过来的数据了
class ThreeScreen extends StatelessWidget { static const routeName = '/extractArguments'; final String title; final String message; ThreeScreen({this.title, this.message}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("second"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text(title), Text(message), ], ), ), ); } }
在这个页面,数据都是通过构造方法中传递了,减少了在页面获取传递数据的代码
class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pushNamed(context, ThreeScreen.routeName, arguments: Photo(title: "args title", message: "args message")); }, child: Text("next screen"), ), ), ); } }
发送方式的代码没有改变
4.接收页面返回值
有的时候我们希望在前一个页面接收另外一个页面的数据,这个怎么处理呢
class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: FirstButton(), ); } } class FirstButton extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: RaisedButton( onPressed: () { Navigator.pushNamed(context, "/second", arguments: Photo(title: "pass title", message: "pass message")) .then((vale) { final snackBar = SnackBar( content: Text('Yay! A SnackBar!'), action: SnackBarAction( label: 'Undo', onPressed: () { }, ), ); Scaffold.of(context).showSnackBar(snackBar); }); }, child: Text("next screen"), ), ); } }
关键的代码是这段,then 方法用来处理接收数据后的处理逻辑,这个例子中主要通过SnackBar 展示一下接收的信息
Navigator.pushNamed(context, "/second", arguments: Photo(title: "pass title", message: "pass message")) .then((vale) { final snackBar = SnackBar( content: Text('Yay! A SnackBar!'), action: SnackBarAction( label: 'Undo', onPressed: () { }, ), ); Scaffold.of(context).showSnackBar(snackBar); });
为什么要单独抽取出FirstButton组件?
是因为SnackBar只能在Scaffold 组件代码中使用会报错
下面代码是用于在推出当前页面的时候,处理了ok 给前一个页面
Navigator.pop(context, "ok");
总结
使用上跟Android 的使用方式类似,有点经验的人掌握这个不是很难
https://docs.flutter.io/flutter/widgets/Navigator-class.html
https://www.raywenderlich.com/110-flutter-navigation-tutorial
作者:饥饿的大灰狼 来源:简书