截至目前为止,我们的Widget都是在一个页面上实现的。对于一个App而言,不可能只有一个页面。那么在Flutter怎样实现页面间的跳转呢?
启动一个新的页面:Navigator.push(),类似Android中的startActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 1class MyApp extends StatelessWidget {
2
3 @override
4 Widget build(BuildContext context) {
5 return MaterialApp(
6 title: 'Flutter App',
7 home: FirstRoute(),
8 );
9 }
10}
11
12class FirstRoute extends StatelessWidget {
13 @override
14 Widget build(BuildContext context) {
15 return Scaffold(
16 appBar: AppBar(
17 title: Text("First Route"),
18 ),
19 body: Center(
20 child: RaisedButton(
21 child: Text("go to second route"),
22 onPressed: () {
23 Navigator.push(context,
24 MaterialPageRoute(builder: (context) => SecondRoute()));
25 }),
26 )),
27 );
28 }
29}
30
31class SecondRoute extends StatelessWidget {
32
33 @override
34 Widget build(BuildContext context) {
35 return Scaffold(
36 appBar: AppBar(
37 title: Text("Second Route"),
38 ),
39 body: Center(
40 child: Column(
41 mainAxisAlignment: MainAxisAlignment.center,
42 children: <Widget>[
43 RaisedButton(
44 child: Text("go back"),
45 onPressed: () {
46
47 }),
48 ],
49 ),
50 ));
51 }
52}
53
Navigator.push()方法接收两个参数:
- BuildContext
-
Route:这里使用了MaterialPageRoute,而这个路由对象有需要一个WidgetBuilder对象:
-
1
2 1typedef WidgetBuilder = Widget Function(BuildContext context);
2
所以只需要返回一个Widget对象即可。可见,在Flutter中,一个Route就是一个Widget。
- 启动新页面时携带数据
在Android中,我们在启动一个新的Activity时,可以使用intent来携带一些数据传递给下一个Activity使用。而在Flutter中更直接:由于我们是直接创建了一个SecondRoute对象作为返回值,那么我们可以直接将需要携带的数据通过构造器传递过去即可。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 1class SecondRoute extends StatelessWidget {
2 SecondRoute({this.data});
3
4 final String data;
5
6 @override
7 Widget build(BuildContext context) {
8 return Scaffold(
9 appBar: AppBar(
10 title: Text("Second Route"),
11 ),
12
13 body: Center(
14 child: Column(
15 mainAxisAlignment: MainAxisAlignment.center,
16 children: <Widget>[
17 RaisedButton(
18 child: Text("go back"),
19 onPressed: () {
20
21 }),
22 Text(data)
23 ],
24 ),
25 ));
26 }
27}
28
29Navigator.push(context, MaterialPageRoute(builder: (context) =>
30 SecondRoute(data: "hello world")));
31
这样我们就可以在SecondRoute中使用我们传入的data了。
- 从当前页面返回: Navigator.pop(),类似Anroid的finish()方法
我们来实现SecondRoute中的RaisedButton的点击回调:
1
2 1Navigator.pop(context);
2
-
从当前页面返回并携带数据到上一个页面:
1
2 1Navigator.pop(context, "Welcome");//直接将要返回的数据作为pop方法的第二个参数
2
那么我们在FirstRoute中怎么去接收页面返回的数据呢,它可没有onActivityResult回调。这里会涉及到Dart语言本身的语法,所以我们需要先停一下。
- await和async
在Android中或者说在Java中,我们要想拿到异步调用的返回结果,通常(Java中也可以通过Future和Callable来实现异步操作带返回值)是通过接口回调的方式,将结果作为回调方法的参数返回回来。就比如对于一个线程,无论是通过继承Thread类还是通过实现Runnable接口的方式,在run方法里我们都不能够直接返回结果。而在Dart中,我们只需要借助一个关键字await:
1
2
3
4 1var info = await queryUserInfo(); // 假定这个queryUserInfo是从数据库中查询或
2 //者网络请求,是异步操作
3showUserInfo(info); // 借助于await关键字,我们可以直接拿到异步操作的结果
4
是不是很强大,而且更加简单!单独的await关键字的使用还不行,我们还需要再使用await的方法上加上async关键字,表示该方法是一个异步方法。
1
2
3
4
5 1loadData() async{
2 var info = await queryUserInfo();
3 showUserInfo(info);
4}
5
await有几个注意点:
-
await后面的表达式一般都返回一个Future类型的对象,即使表达式本身返回的不是一个Future对象,也会被编译器自动包装成一个Future对象
- await并不是会一直停在那等着,因为这样它不就会白白占据CPU的资源吗?相当于会进入一个阻塞的状态,并且await后面的代码也不会执行,会等到await有返回值,才会继续执行。
-
回到我们之前的问题中来,有了第5条知识点,我们可以有如下代码:
1
2
3
4
5
6 1_navigateAndDisplayResult(BuildContext context) async {
2 final result = await Navigator.push(
3 context, MaterialPageRoute(builder: (context) =>
4 SecondRoute("hello world")));//这个result就是"welcome"
5}
6
-
Flutter还支持通过路由名来实现页面跳转:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1MaterialApp(
2 // 声明路由映射
3 routes: {SecondRoute.routeName: (context) => SecondRoute()},
4 title: 'Flutter Demo',
5 home: FirstRoute(),
6);
7
8class SecondRoute extends StatelessWidget {
9 static const routeName = '/second';
10}
11
12//使用处
13Navigator.pushNamed(context, SecondRoute.routeName);
14
这种方式的优点就是耦合性低,更加直观,缺点就是不能动态在页面间传递数据。