cubegao

Flutter 嵌套过深的解决方案

2020-01-03

写代码容易,读代码难。功能是都实现了,但是对维护人员来说,简直就是灾难。

##背景
Flutter 注重组合而非继承,要想搭建出 UI,需要组合不同功能的 Widget,如布局 Widget、响应 Widget、控件 Widget 等才能搭建出一个功能完善的UI界面,这便导致了嵌套地狱: 在顶级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
class FrostedGlass extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(
children: <Widget>[
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: new FlutterLogo()),
new Center(
child: new ClipRect(
child: new BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Opacity(
opacity: 0.5,
child: new Container(
width: 200.0,
height: 200.0,
decoration: new BoxDecoration(
color: Colors.grey.shade200,
),
child: new Center(
child: new Text('Frosted',
style: Theme.of(context).textTheme.headline2),
),
),
),
),
),
),
],
),
);
}
}

IDE上面看到是这样的。
23FE5157A35842B9E575CC472D30A4E7.jpg

##方法一
使用变量、方法与自定义 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
// 把嵌套严重的 Widget 的一部分提出一个新的 Widget
class A xxxx {
// 使用方法抽离部分 Widget
Widget constructD() {
return new D(
child:new E(
child:new F(
child:new G(
...
),
),
),
),
}

@override
Widget build(BuildContext context) {
return new B(
child:new C(
child: constructD()
),
);
}
}

##方法二
widget外面再包一层。请求自己的class,返回封装好的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
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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
import 'package:flutter/material.dart';

///widget装饰器
class WidgetDecoration {
Widget _widget;

WidgetDecoration(Widget widget) {
this._widget = widget;
}

Function _onTapFunc;
Function _onDoubleTapFunc;
Function _onLongPressFunc;

///add padding属性
WidgetDecoration padding(
{Key key, double left = 0.0, double top = 0.0, double right = 0.0, double bottom = 0.0}) {
var padding = EdgeInsets.only(left: left, top: top, right: right, bottom: bottom);
_widget = new Padding(key: key, padding: padding, child: _widget);
return this;
}

///增加padingall
WidgetDecoration paddAll({Key key, double all = 0.0}) {
var padding = EdgeInsets.all(all);
_widget = new Padding(key: key, padding: padding, child: _widget);
return this;
}

///增加align 当前布局相对位置
///FractionalOffset.centerRight
WidgetDecoration align({Key key, AlignmentGeometry alignment = Alignment.center}) {
_widget = new Align(key: key, alignment: alignment, child: _widget);
return this;
}

///位置
WidgetDecoration positioned(
{Key key,
double left,
double top,
double right,
double bottom,
double width,
double height}) {
_widget = new Positioned(
key: key,
left: left,
top: top,
right: right,
bottom: bottom,
width: width,
height: height,
child: _widget);
return this;
}

///stack 相当于frameLayout布局

///填充布局
WidgetDecoration expanded({Key key, int flex = 1}) {
_widget = new Expanded(key: key, flex: flex, child: _widget);
return this;
}

///是否显示布局 true为不显示 false为显示
WidgetDecoration offstage({Key key, bool offstage = true}) {
_widget = new Offstage(key: key, offstage: offstage, child: _widget);
return this;
}

///透明度 0 是完全透明 1 完全不透明
WidgetDecoration opacity({Key key, @required double opacity, alwaysIncludeSemantics = false}) {
_widget = new Opacity(
key: key, opacity: opacity, alwaysIncludeSemantics: alwaysIncludeSemantics, child: _widget);
return this;
}

///基准线布局
WidgetDecoration baseline({
Key key,
@required double baseline,
@required TextBaseline baselineType,
}) {
_widget =
new Baseline(key: key, baseline: baseline, baselineType: baselineType, child: _widget);
return this;
}

///设置宽高比
WidgetDecoration aspectRatio({Key key, @required double aspectRatio}) {
_widget = new AspectRatio(key: key, aspectRatio: aspectRatio, child: _widget);
return this;
}

///矩阵转换
WidgetDecoration transform({
Key key,
@required Matrix4 transform,
origin,
alignment,
transformHitTests = true,
}) {
_widget = new Transform(
key: key,
transform: transform,
origin: origin,
alignment: alignment,
transformHitTests: transformHitTests,
child: _widget);
return this;
}

///居中 todo: center
WidgetDecoration center({Key key, double widthFactor, double heightFactor}) {
_widget =
new Center(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: _widget);
return this;
}

///布局容器
WidgetDecoration container({
Key key,
alignment,
padding,
Color color,
Decoration decoration,
foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
margin,
transform,
}) {
_widget = new Container(
key: key,
alignment: alignment,
padding: padding,
color: color,
decoration: decoration,
foregroundDecoration: foregroundDecoration,
width: width,
height: height,
constraints: constraints,
margin: margin,
transform: transform,
child: _widget);
return this;
}

///设置具体尺寸
WidgetDecoration sizedBox({Key key, double width, double height}) {
_widget = new SizedBox(key: key, width: width, height: height, child: _widget);
return this;
}

///设置最大最小宽高布局
WidgetDecoration constrainedBox({
Key key,
minWidth = 0.0,
maxWidth = double.infinity,
minHeight = 0.0,
maxHeight = double.infinity,
}) {
BoxConstraints constraints = new BoxConstraints(
minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight);
_widget = new ConstrainedBox(key: key, constraints: constraints, child: _widget);
return this;
}

///限定最大宽高布局
WidgetDecoration limitedBox({
Key key,
maxWidth = double.infinity,
maxHeight = double.infinity,
}) {
_widget = new LimitedBox(key: key, maxWidth: maxWidth, maxHeight: maxHeight, child: _widget);
return this;
}

///百分比布局
WidgetDecoration fractionallySizedBox(
{Key key, alignment = Alignment.center, double widthFactor, double heightFactor}) {
_widget = new FractionallySizedBox(
key: key,
alignment: alignment,
widthFactor: widthFactor,
heightFactor: heightFactor,
child: _widget);
return this;
}

///缩放布局
WidgetDecoration fittedBox({Key key, fit = BoxFit.contain, alignment = Alignment.center}) {
_widget = new FittedBox(key: key, fit: fit, alignment: alignment, child: _widget);
return this;
}

///旋转盒子 1次是90度
WidgetDecoration rotatedBox({
Key key,
@required int quarterTurns,
}) {
_widget = new RotatedBox(key: key, quarterTurns: quarterTurns, child: _widget);
return this;
}

///装饰盒子 细节往外抛 decoration 编写放在外面
WidgetDecoration decoratedBox({
Key key,
@required Decoration decoration,
position = DecorationPosition.background,
}) {
_widget =
new DecoratedBox(key: key, decoration: decoration, position: position, child: _widget);
return this;
}

///圆形剪裁
WidgetDecoration clipOval(
{Key key, CustomClipper<Rect> clipper, Clip clipBehavior = Clip.antiAlias}) {
_widget = new ClipOval(key: key, clipper: clipper, clipBehavior: clipBehavior, child: _widget);
return this;
}

///圆角矩形剪裁
WidgetDecoration clipRRect(
{Key key,
@required BorderRadius borderRadius,
CustomClipper<RRect> clipper,
Clip clipBehavior = Clip.antiAlias}) {
_widget = new ClipRRect(
key: key,
borderRadius: borderRadius,
clipper: clipper,
clipBehavior: clipBehavior,
child: _widget);
return this;
}

///矩形剪裁 todo: 需要自定义clipper 否则无效果
WidgetDecoration clipRect(
{Key key, @required CustomClipper<Rect> clipper, Clip clipBehavior = Clip.hardEdge}) {
_widget = new ClipRect(key: key, clipper: clipper, clipBehavior: clipBehavior, child: _widget);
return this;
}

///路径剪裁 todo: 需要自定义clipper 否则无效果
WidgetDecoration clipPath(
{Key key, @required CustomClipper<Path> clipper, Clip clipBehavior = Clip.antiAlias}) {
_widget = new ClipPath(key: key, clipper: clipper, clipBehavior: clipBehavior, child: _widget);
return this;
}

///animatedOpacity 淡入淡出
WidgetDecoration animatedOpacity({
Key key,
@required double opacity,
Curve curve = Curves.linear,
@required Duration duration,
}) {
_widget = new AnimatedOpacity(
key: key, opacity: opacity, curve: curve, duration: duration, child: _widget);
return this;
}

///页面简单切换效果
WidgetDecoration hero({Key key, @required Object tag}) {
_widget = new Hero(key: key, tag: tag, child: _widget);
return this;
}

///点击事件
WidgetDecoration onClick({Key key, onTap, onDoubleTap, onLongPress}) {
_widget = new GestureDetector(
key: key,
child: _widget,
onTap: onTap ?? _onTapFunc,
onDoubleTap: onDoubleTap ?? _onDoubleTapFunc,
onLongPress: onLongPress ?? _onLongPressFunc,
);
return this;
}

///添加点击事件
WidgetDecoration onTap(Function func, {Key key}) {
_onTapFunc = func;
_widget = new GestureDetector(
key: key,
child: _widget,
onTap: _onTapFunc,
onDoubleTap: _onDoubleTapFunc,
onLongPress: _onLongPressFunc,
);
return this;
}

///双击
WidgetDecoration onDoubleTap(Function func, {Key key}) {
_onDoubleTapFunc = func;
_widget = new GestureDetector(
key: key,
child: _widget,
onTap: _onTapFunc,
onDoubleTap: _onDoubleTapFunc,
onLongPress: _onLongPressFunc,
);
return this;
}

///长按
WidgetDecoration onLongPress(Function func, {Key key}) {
_onLongPressFunc = func;
_widget = new GestureDetector(
key: key,
child: _widget,
onTap: _onTapFunc,
onDoubleTap: _onDoubleTapFunc,
onLongPress: _onLongPressFunc,
);
return this;
}

Widget build() {
return _widget;
}
}

##方法三
用扩展函数来给系统widget添加一个child属性。
使用时也支持链式调用。

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
extension WidgetExt on Widget {

Container intoContainer({
//复制Container构造函数的所有参数(除了child字段)
Key key,
AlignmentGeometry alignment,
EdgeInsetsGeometry padding,
Color color,
Decoration decoration,
Decoration foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
EdgeInsetsGeometry margin,
Matrix4 transform,
}) {
//调用Container的构造函数,并将当前widget对象作为child参数
return Container(
key: key,
alignment: alignment,
padding: padding,
color: color,
decoration: decoration,
foregroundDecoration: foregroundDecoration,
width: width,
height: height,
constraints: constraints,
margin: margin,
transform: transform,
child: this,
);
}
}

Tags: Flutter

扫描二维码,分享此文章