Flutter开发者

Flutter中的路由跳转

2018-04-02

上期回顾

在前一篇的文章我们学习了ListView和GridView的用法,我们可以使用new 方式和ListView.builder()、ListView.custom()的方式来构建这两个Widget,使用ListVIew和GridView可以帮助我们快速构建多Item视图。

在前面的文章中我们的例子都是在一个界面来处理的,但是我们用过的应用中大多数都不肯是一个界面啊,所以本篇文章就会介绍下多个界面应用直接必须要用到的路由与跳转。

静态路由

在Flutter中有着两种路由跳转的方式,一种是静态路由,在创建时就已经明确知道了要跳转的页面和值。另一种是动态路由,跳转传入的目标地址和要传入的值都可以是动态的。

OK,还是先来介绍下静态路由

从我们开始学习Flutter到现在,相信大家看到最多的肯定是下面的代码

1
2
3
void main(){
runApp(new MaterialApp());
}

在runApp方法中需要传入一个MaterialApp的Widget,但是我们基本用到的都是home属性,但是其实MaterialApp方法里面有着很多的参数,其中routes参数就是定义路由的参数。

routes: const {}

routes需要传入类型的Map,第一个参数是目标路由的名称,第二个参数就是你要跳转的页面。

嗯,还是来个例子看看怎么用

第一个页面:

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
import 'package:flutter/material.dart';
import 'package:test1/route/Page2.dart';

void main() {
runApp(new MaterialApp(
home: new MyApp(),
routes: <String, WidgetBuilder>{
'/page2': (BuildContext context) => new Page2(),
},
));
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Page1"),
),
body: new Center(
child: new RaisedButton(
onPressed: () {
Navigator.of(context).pushNamed("/page2");
},
child: new Text("点我跳转"),
color: Colors.blue,
highlightColor: Colors.lightBlue,
),
),
);
}
}

在第一个页面在Main方法中我们定义了我们需要跳转的页面名称为“Page2”,要跳转的页面是Page2,每当我们点击屏幕正中央的button都会触发调用

Navigator.of(context).pushNamed(“/page2”);

Navigator就是在在Flutter中负责页面导航的,相信了解Android的童鞋都知道这个玩意。
使用pushNamed方法传入一个在routes定义的名字即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import 'package:flutter/material.dart';
class Page2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Page2"),
),
body: new Center(
child: new Text(
"Page2",
style: new TextStyle(fontSize: 25.0),
),
),
);
}
}

好了,来看下效果:

好吧,那么尝试下往下个页面传递数据,其实也很简单,我们给第二个页面加一个构造函数,并且把从第一个页面传递过来的值赋给Text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import 'package:flutter/material.dart';

class Page2 extends StatelessWidget {
final title;

Page2(this.title);

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Page2"),
),
body: new Center(
child: new Text(
title,
style: new TextStyle(fontSize: 25.0),
),
),
);
}
}

在第一个页面定义路由时就需要把你需要传递的参数定义出来。

1
2
3
routes: <String, WidgetBuilder>{
'/page2': (BuildContext context) => new Page2("I am from Page1"),
},

这种定义路由并使用的方式非常的简单,但是大家肯定会发现一个问题,就是如果我需要传递给第二个页面的数据不是已知的话我就无法使用这种方式,因为我们无法动态改变上面定义的值。

所以,我们就需要了解下Flutter中的动态路由了。

动态路由

在Navigator中还有一个方法是push()方法,需要传入一个Route对象,在Flutter中我们可以使用PageRouteBuilder来构建这个Route对象。

所以,我们可以在Button的点击事件中做下面代码的操作:

1
2
3
4
5
6
7
8

Navigator.of(context).push(new PageRouteBuilder(
pageBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation) {
return new Page2("some attrs you like ");

}))

这样的话,我们就可以把用户操作与交互的数据传递给下个页面。

还是结合前面的讲过的来举个例子。

在前面的文章中,我们使用TextField举过一个例子,对用户输入的用户名密码进行判断,当用户名是“flyou”,密码是“admin”时提示登录成功,否则提示登录失败。

今天我们稍微改动下以前这个例子,当用户名与密码相同时提示正确,否则就提示用户名密码有误。输入正确则直接跳转到第二个页面,并把登录成功的用户名给传递过去。

下面代码提到的DynamicPage就是我们第二个页面。

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import 'package:flutter/material.dart';
import 'package:test1/route/DynamicPage.dart';


void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}

class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new MyAppState();
}
}

class MyAppState extends State<MyApp> {
TextEditingController _userNameController = new TextEditingController();
TextEditingController _userPasswordController = new TextEditingController();

void onTextClear() {
setState(() {
_userNameController.text = "";
_userPasswordController.text = "";
});
}

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("TextField"),
),
body: new Column(
children: <Widget>[
new TextField(
controller: _userNameController,
decoration: new InputDecoration(
contentPadding: const EdgeInsets.only(top: 10.0),
icon: new Icon(Icons.perm_identity),
labelText: "请输入用户名",
helperText: "注册时填写的名字"),
),
new TextField(
controller: _userPasswordController,
decoration: new InputDecoration(
contentPadding: const EdgeInsets.only(top: 10.0),
icon: new Icon(Icons.lock),
labelText: "请输入密码",
helperText: "登录密码"),
obscureText: true,
),
new Builder(builder: (BuildContext context) {
return new RaisedButton(
onPressed: () {
var userName= _userNameController.text.toString();
var passWord= _userPasswordController.text.toString();
if (userName == passWord ) {
Navigator.of(context).push(new PageRouteBuilder(
pageBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation) {
return new DynamicPage(userName);

}));
} else {
Scaffold.of(context).showSnackBar(
new SnackBar(content: new Text("登录失败,用户名密码有误")));
}
onTextClear();
},
color: Colors.blue,
highlightColor: Colors.lightBlueAccent,
disabledColor: Colors.lightBlueAccent,
child: new Text(
"登录",
style: new TextStyle(color: Colors.white),
));
})
],
));
}
}

每当我们点击登录按钮时都会判断用户名密码是否相等,如果相等则把用户输入的用户名传递给二个页面并显示出来。

第二个页面的代码非常的简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'package:flutter/material.dart';

class DynamicPage extends StatelessWidget {
final userName;

DynamicPage(this.userName);

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("DynamicPage"),
),
body: new Center(
child: new Text("登录成功,欢迎新用户:$userName")),
);
}
}

页面出栈

在Flutter中我们可以使用Navigator.of(context).pop()进行出栈操作,但是值得注意的时如果页面上有Dialog、BottomSheet、popMenu类似的Widget使用pop()方法会优先进行这些Widget的关闭操作。

还是上面的代码,我们把第二个页面的Text改成一个按钮,每当点击这个按钮就关闭掉这个页面。

1
2
3
4
5
6
7
8
9
10
body: new Center(
child: new RaisedButton(
child: new Text("点我返回"),
onPressed: () {
Navigator.of(context).pop();
},
color: Colors.blue,
highlightColor: Colors.lightBlue,
),
)

处理出栈页面返回值

在前面我们介绍到Navigator.of(context).pop()可以使得页面出栈,当然这个pop方法也是可以传值的,只用Navigator.of(context).pop(attrs)就可以传入自己想要返回的值

第二个页面,我们调用

Navigator.of(context).pop("我是第二个页面返回的数据:$title");

在第一个页面我们接收第二个页面返回的值

1
2
3
4
5
6
7
8
Future future = Navigator.of(context).pushNamed("/pageB");
future.then((value) {
showDialog(
context: context,
child: new AlertDialog(
title: new Text(value),
));
}

这里我们实用到了Future 对象(会面会具体讲到),来处理第二个页面返回的值,当第一个页面收到第二个页面返回的值时,就会弹出AlertDialog并显示从第二个页面返回的值

这样我们就可以根据第二个页面返回的值做相应的操作,如果你需要在接到返回值后更细界面,你需要使你的Widget继承StatefulWidget。

小结

  • 使用Navigator.of(context).pushName(“/“)可以进行静态路由的跳转
  • 使用push(Route)可以进行态路由的跳转
  • 动态路由可以传入未知数据
  • 使用pop()可以进行路由的出栈并且可以传递参数
  • 可以使用Future接收上个页面返回的值。
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

打赏请备注姓名或者昵称,方便我后期统计哦

关注公众号,及时查阅最新文章