Flutter学习日记-log2

本文最后更新于:3 个月前

Flutter中文网


日志随笔

一、复习第一天内容

  • StatefulWidget修改State状态,需要刷新build方法,所以需要单独创建State,并且把build方法写在State
  • asyncawait必须成对使用,否则VS Code会提示fix

二、命名路由

  • MaterialApp类存在属性routes,代表路由表,值是键值对Map形式
  • initialRoute字段定义初始页面别名,在路由表routes中配置相应的初始页面别名,即可以正常进入初始页面
  • Navigator.pushNamed方法可以直接使用路由别名进行跳转
1
2
3
4
5
6
7
8
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
"pageName": (context) => PageRoute()
}
);
}

三、路由传参的拓展

1、接受参数的两种方式和分别对应的发送参数方法

1. 接受方法在build方法外部,使用指定的语法直接接收
  • 接收参数的方法
1
2
3
4
5
ParamsRoute({
Key key,
@required this.params
}) : super(key: key);
final params;
  • 发送参数的方法
1
2
3
4
5
6
7
// 第一种方式
onPressed: () async {
var backparams = await Navigator.push(context, MaterialPageRoute(builder: (newpage) {
return ParamsRoute(params: "");
}));
print(backparams);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 第二种路由方式
@override
Widget build(BuildContext context) {
return MaterialApp(
// 先注册路由表
routes: {
"ParamsRoute": (context) {
return ParamsRoute(params: ModalRoute.of(context).settings.arguments);
}
}
);
}

// 传参调用方法
onPressed: () async {
var backparams = await Navigator.of(context).pushNamed("ParamsRoute", arguments: "");
print(backparams);
}
  1. 通过build方法中的context参数,使用ModalRoute.of的方法读取其中的参数值
  • 接收参数的方法
1
2
3
Widget build(BuildContext context) {
var params = ModalRoute.of(context).settings.arguments;
}
  • 只能使用路由表传参
1
2
3
4
5
6
7
8
9
10
11
12
13
// 第二种路由方式
// 先注册路由表
routes: {
"ParamsRoute": (context) {
return ParamsRoute(params: ModalRoute.of(context).settings.arguments);
},
}

// 传参调用方法
onPressed: () async {
var backparams = await Navigator.of(context).pushNamed("ParamsRoute", arguments: "");
print(backparams);
}

2、未注册路由表键值对

  • 如果没有在路由表中找到,那么会触发onGenerateRoute方法,此方法中会获取到触发的命名路由的名称,在此方法内生成新的路由地址返回即可
  • 官方建议使用命名路由,使用onGenerateRoute统一入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@override
Widget build(BuildContext context) {
return MaterialApp(
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
if (settings.name == 'NewPageRoute') {
builder = (BuildContext context) => NewPageRoute();
} else {
print("没有对应的页面");
}
return MaterialPageRoute(builder: builder, settings: settings);
}
);
}

以上代码如下:

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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// home: MyHomePage(title: 'Flutter Demo Home Page'),

initialRoute: "homeRoute",
//注册路由表
routes: {
"homeRoute": (context) => MyHomePage(title: "Flutter Demo Home Page"),
"newpage1": (context) => NewRoute(),
"newpage2": (context) => EchoRoute(),
"newpage23": (context) {
return TipRoute(tip: ModalRoute.of(context).settings.arguments);
},
"newpage4": (context) => ForthPage(),
},
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
if (settings.name == 'newpage5') {
builder = (BuildContext context) => FifthPage();
} else {
print("没有对应的页面");
}
return MaterialPageRoute(builder: builder, settings: settings);
},
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text(
"open first new page",
),
onPressed: () {
Navigator.pushNamed(context, "newpage1");
},
),
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text(
"open second new page",
),
onPressed: () {
Navigator.of(context).pushNamed("newpage2", arguments: "hi");
},
),
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text(
"open third new page",
),
onPressed: () async {
var backparams = await Navigator.push(context,
MaterialPageRoute(builder: (newpage) {
return TipRoute(tip: "333");
}));
print(backparams);
},
),
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text(
"open third new page2",
),
onPressed: () async {
var backparams = await Navigator.of(context).pushNamed("newpage23", arguments: "2233");
print(backparams);
},
),
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text(
"open third new page4",
),
onPressed: () async {
var backparams = await Navigator.of(context).pushNamed("newpage4", arguments: "4444");
print(backparams);
},
),
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text(
"open third new page5",
),
onPressed: () async {
var backparams = await Navigator.of(context).pushNamed("newpage5", arguments: "5555");
print(backparams);
},
),
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text(
"open third new page6",
),
onPressed: () {
Navigator.of(context).pushNamed("newpage6", arguments: "6666");
},
),
],
),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}

class NewRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("new page"),
),
body: null,
);
}
}

class EchoRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
var args = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: AppBar(
title: Text("new page2EchoRoute"),
),
body: Text(args),
);
}
}

class TipRoute extends StatelessWidget {
TipRoute({Key key, @required this.tip}) : super(key: key);
final tip;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("new page3TipRoute"),
),
body: Column(
children: <Widget>[
Text(tip),
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text(
"back and send params",
),
onPressed: () {
Navigator.pop(context, tip);
},
),
],
),
);
}
}

class ForthPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var args = ModalRoute.of(context).settings.arguments;

return Scaffold(
appBar: AppBar(
title: Text("第四个页面"),
),
body: Column(
children: <Widget>[
Text(args),
FlatButton(
child: Text("back and send params"),
color: Colors.blue,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context, args);
},
),
],
),
);
}
}

class FifthPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var args = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: AppBar(
title: Text("第五个页面"),
),
body: Column(
children: <Widget>[
Text(args),
FlatButton(
child: Text("back and send params"),
color: Colors.blue,
textColor: Colors.white,
onPressed: () {
Navigator.pop(context, args);
},
),
],
),
);
}
}

四、包管理

  • pubspec.yaml文件
  • Pub仓库,类似于node中的npm仓库
  • VS Code中可以直接编辑pubspec.yaml文件,每次修改保存后,会自动触发flutter packages get方法,也可以使用命令行手动触发
  • 需要注意dependenciesdev_dependencies的区别,前者的依赖包将作为APP的源码的一部分参与编译,生成最终的安装包。而后者的依赖包只是作为开发阶段的一些工具包,主要是用于帮助我们提高开发、测试效率,比如flutter的自动化测试包

四、资源管理和调试工具

  • 资源管理
  • flutter调试
    • printdebugPrint都可以打印,建议使用debugPrint,避免输出内容过多时Android丢失部分内容
    • debugDumpApp输出Widgets
    • debugDumpRenderTree输出渲染树
    • debugDumpLayerTree输出Layer
    • debugDumpSemanticsTree输出语义树
  • 统计应用启动时间
    • $ flutter run --trace-startup --profile,结果保存到build目录下start_up_info.json文件

五、State生命周期

image_1

六、组件库

  • iOS Cupertino风格
  • Android Material风格

1、基础组件

  • Text:该组件可让您创建一个带格式的文本。
  • RowColumn: 这些具有弹性空间的布局类Widget可让您在水平(Row)和垂直(Column)方向上创建灵活的布局。其设计是基于Web开发中的Flexbox布局模型。
  • Stack: 取代线性布局,Stack允许子widget堆叠, 你可以使用Positioned来定位他们相对于Stack的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位(absolute positioning)布局模型设计的。
  • ContainerContainer可让您创建矩形视觉元素。container 可以装饰一个BoxDecoration, 如 background、一个边框、或者一个阴影。Container也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外,Container可以使用矩阵在三维空间中对其进行变换

2、Material组件

  • MaterialAppScaffoldAppBarFlatButton
  • 初始页面推荐使用MaterialApp

七、状态管理

  • 如果状态是用户数据,如复选框的选中状态、滑块的位置,则该状态最好由父Widget管理
  • 如果状态是有关界面外观效果的,例如颜色、动画,那么状态最好由Widget本身来管理
  • 如果某一个状态是不同Widget共享的则最好由它们共同的父Widget管理
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
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Row(
children: <Widget>[
TapboxA(),
ParentWidget(),
],
),
);
}
}

class TapboxA extends StatefulWidget {
@override
_TapboxAState createState() => new _TapboxAState();
}

class _TapboxAState extends State<TapboxA> {
bool _selected = true;
void _refreshSeleced() {
setState(() {
_selected = !_selected;
});
}

@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
padding: EdgeInsets.all(5),
color: Colors.orange,
child: FlatButton(
child: Text(
_selected ? '00' : '99',
style: TextStyle(
fontSize: 40,
color: Colors.white,
),
),
color: _selected ? Colors.blue : Colors.red,
onPressed: () {
_refreshSeleced();
},
),
);
}
}

class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => new _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
bool _selected = true;
void _refreshSeleced(bool selected) {
setState(() {
_selected = selected;
});
}

@override
Widget build(BuildContext context) {
return TapboxB(
selected: _selected,
selectedValueChanged: _refreshSeleced,
);
}
}

class TapboxB extends StatelessWidget {
final bool selected;
final ValueChanged<bool> selectedValueChanged;

TapboxB({
Key key,
this.selected,
this.selectedValueChanged,
}):super(key: key);

void _refreshSelecedB() {
selectedValueChanged(!selected);
}

@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
padding: EdgeInsets.all(5),
color: Colors.orange,
child: FlatButton(
child: Text(
selected ? '00' : '99',
style: TextStyle(
fontSize: 40,
color: Colors.white,
),
),
color: selected ? Colors.blue : Colors.red,
onPressed: () {
_refreshSelecedB();
},
),
);
}
}

未完待续