概述

路由在我们开发的App中起着”起承转合”的作用,今天这篇文章总结一下Flutter的路由使用,通过阅读本文,你能了解到如下内容:

  • 使用Hero制作动画转场。
  • Push 和 back。
  • 通过namePush转场。
  • 跳转到下个页面并携带参数。
  • 返回并带回参数。
  • 将数据发送到新页面。

Hero

通过Hero标签指定两个锚点,在两个页面转换的过程中展示转场动画。

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

class NavigationHero extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'hero 转场',
home: MainScreen(),
);
}
}

class MainScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('main screen'),
),
body: GestureDetector(
child: Center(
// 关键代码
child: Hero(
tag: 'imageHero',
child: Image.network('https://picsum.photos/250?image=9'))),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) {
return DetailScreen();
},
fullscreenDialog: true));
},
));
}
}

class DetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
// 关键代码
child: Hero(
tag: 'imageHero',
child: Image.network('https://picsum.photos/250?image=9')),
onTap: () {
Navigator.pop(context);
},
)),
);
}
}

执行效果

hero

在iOS项目中有个类似的Swift框架叫Hero,感兴趣的朋友可以去GitHub查看。

Push and Back

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

class NavigationPushAndBack extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FirstRoute();
}
}

class FirstRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: Center(
child: RaisedButton(
onPressed: () {
//关键代码
Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondRoute()));
},
child: Text('Open Route'),
),
),
);
}
}

class SecondRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Sencond Route'),
),
body: Center(
child: RaisedButton(
onPressed: () {
//关键代码
Navigator.pop(context);
},
child: Text('Go Back'),
),
),
);
}
}

执行效果

push&back

导航的时候携带参数

跳转的时候携带参数有两种方式:

  • 通过push的时候携带RouteSettings
  • 通过pushNamed在onGenerateRoute中配置参数。
  • onGenerateRoute配置有些
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import 'package:flutter/material.dart';

class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}

class PassArgumentScreen extends StatelessWidget {
static const routeName = '/passArguments';

final String title;
final String message;

const PassArgumentScreen(
{Key key, @required this.title, @required this.message})
: super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
);
}
}

class ExtractArgumentsScreen extends StatelessWidget {
static const routeName = '/extractArguments';

@override
Widget build(BuildContext context) {
final ScreenArguments args = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: AppBar(
title: Text(args.title),
),
body: Center(
child: Text(args.message),
),
);
}
}

class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳转到 extracts arguments'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) {
return ExtractArgumentsScreen();
},
settings: RouteSettings(
arguments: ScreenArguments('Extract Arguments Screen',
'This message is extracted in the build method'),
)));
},
),
RaisedButton(
child: Text('Navigate to a named that accepts arguments'),
onPressed: () {
Navigator.pushNamed(context, PassArgumentScreen.routeName,
arguments: ScreenArguments('Accept Arguments Screen',
'This message is extracted in the onGenerateRoute function.'));
},
)
],
),
),
);
}
}

class PassArgumentsScreen extends StatelessWidget {
static const routeName = '/passArguments';
final String title;
final String message;

const PassArgumentsScreen(
{Key key, @required this.title, @required this.message})
: super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
);
}
}


//main.dart
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
onGenerateRoute: (settings) {
if(settings.name == PassArgumentsScreen.routeName) {
final ScreenArguments args = settings.arguments;

return MaterialPageRoute(
builder: (context) {
return PassArgumentScreen(
title: args.title,
message: args.message,
);
}
);
}
},
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Home()
);
}
}

效果图:

携带参数

页面返回的时候携带参数

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

class NavigationReturnDataFromScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Return Data Demo'),
),
body: Center(child: SelectionButton()),
);
}
}

class SelectionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
_navigateAndDisplaySelection(context);
},
child: Text('Pick an option, any option!'),
);
}

_navigateAndDisplaySelection(BuildContext context) async {
final result = await Navigator.push(
context, MaterialPageRoute(builder: (context) => SelectionScreen()));
Scaffold.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text("$result")));
}
}

class SelectionScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Pick an option'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: () {
Navigator.pop(context, 'Yep!');
},
child: Text('Yep!'),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: () {
Navigator.pop(context, 'Nope.');
},
child: Text('Nope.'),
),
)
],
),
),
);
}
}

运行效果:

backData

将数据发送到新页面

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

class NavigationSendDataToScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: TodosScreen(
todos: List.generate(
20,
(i) => Todo('Todo $i',
'A description of what needs to be done for Todo $i'))),
);
}
}

class Todo {
final String title;
final String description;

Todo(this.title, this.description);
}

class TodosScreen extends StatelessWidget {
final List<Todo> todos;

TodosScreen({Key key, @required this.todos}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(
todo: todos[index],
)));
});
}));
}
}

class DetailScreen extends StatelessWidget {
final Todo todo;
DetailScreen({Key key, @required this.todo}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(todo.title),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Text(todo.description),
),
);
}
}

效果图:

sendDataToNewScreen

参考资料

Swift Hero

flutter-navigation