接下来这段时间,学习一下Flutter众多widget的其中几个的源码,大概了解下其基本面貌

举几个栗子: MaterialApp Scaffold StatelessWidget StatefulWidget AppBar Text Column Row Image ListView Container
其中,他们的继承关系如下所示:

MaterialApp

1
2
3
4
5
class MaterialApp extends StatefulWidget 
abstract class StatefulWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

Scaffold

1
2
3
4
5
6
class Scaffold extends StatefulWidget {
abstract class StatefulWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

StatelessWidget

1
2
3
4
abstract class StatelessWidget extends Widget 
abstract class Widget extends DiagnosticableTree
abstract class DiagnosticableTree with Diagnosticable
mixin Diagnosticable

StatefulWidget

1
2
3
4
abstract class StatefulWidget extends Widget 
abstract class Widget extends DiagnosticableTree
abstract class DiagnosticableTree with Diagnosticable
mixin Diagnosticable

AppBar

1
2
3
4
5
6
class AppBar extends StatefulWidget implements PreferredSizeWidget {
abstract class StatefulWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

Text

1
2
3
4
5
6
class Text extends StatelessWidget {
abstract class StatelessWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

Column

1
2
3
4
5
6
7
8
class Column extends Flex {
class Flex extends MultiChildRenderObjectWidget {
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
abstract class RenderObjectWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

Row

1
2
3
4
5
6
7
8
class Row extends Flex {
class Flex extends MultiChildRenderObjectWidget {
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
abstract class RenderObjectWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

Image

1
2
3
4
5
6
class Image extends StatefulWidget {
abstract class StatefulWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

ListView

1
2
3
4
5
6
7
8
class ListView extends BoxScrollView {
abstract class BoxScrollView extends ScrollView {
abstract class ScrollView extends StatelessWidget {
abstract class StatelessWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

Container

1
2
3
4
5
class Container extends StatelessWidget {
abstract class StatelessWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

从以上可以看出来大部分widget都有个通用的继承关系。

1
2
3
4
5
6
class XXXX extends StatelessWidget/StatefulWidget {
abstract class StatelessWidget/StatefulWidget extends Widget {
abstract class Widget extends DiagnosticableTree {
abstract class DiagnosticableTree with Diagnosticable {
mixin Diagnosticable {

ok,那我们就从这点学习:

在源码中,StatelessWidget StatefulWidget 都继承自 Widget
Widget 又继承自DiagnosticableTree ,而DiagnosticableTree 类会混入Diagnosticable

那我们先看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
@immutable
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key? key;

@protected
@factory
Element createElement();

@override
String toStringShort() {
final String type = objectRuntimeType(this, 'Widget');
return key == null ? type : '$type-$key';
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}

@override
@nonVirtual
bool operator ==(Object other) => super == other;

@override
@nonVirtual
int get hashCode => super.hashCode;

static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}

static int _debugConcreteSubtype(Widget widget) {
return widget is StatefulWidget ? 1 :
widget is StatelessWidget ? 2 :
0;
}
}

从第一行开始:

发现会有几个注解: @nonVirtual @override @immutable @protected @factory

这个注解唯一作用就是静态分析出来代码类型。

同时这个类是被关键字abstract修饰的,代表该类抽象的:

1、抽象类不能被实例化,只有继承它的子类可以。

2、抽象类中一般我们把没有方法体的方法称为抽象方法。

3、子类继承抽象类必须实现它的抽象方法。

4、如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。

里面有个可选参数key,通过这个key ,我们可以获取到该组件(可以获取到该组件的宽高属性、位置属性、方法、state值等),如:

currentContext: 可以找到包括renderBox在内的各种element有关的东西

currentWidget: 可以得到widget的属性

currentState: 可以得到state里面的变量

key的相关使用可见该链接

接着,里面有个抽象方法createElement():

1
2
3
4
@protected
@factory
Element createElement();

其子类StatelessWidget 继承并重写,返回 StatelessElement

1
2
3
@override
StatelessElement createElement() => StatelessElement(this);

其子类StatefulWidget继承并重写,返回 StatefulElement

1
2
3
@override
StatefulElement createElement() => StatefulElement(this);

接着,里面有个单纯描述的方法toStringShort

1
2
3
4
5
6
7
/// A short, textual description of this widget.
@override
String toStringShort() {
final String type = objectRuntimeType(this, 'Widget');
return key == null ? type : '$type-$key';
}

然后

1
2
3
4
5
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}

主要是设置DiagnosticableTree 的一些特性,设置properties.defaultDiagnosticsTreeStyle DiagnosticsTreeStyle.dense,其中DiagnosticsTreeStyle为一个枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum DiagnosticsTreeStyle {
none,
sparse,
offstage,
dense,
transition,
error,
whitespace,
flat,
singleLine,
errorProperty,
shallow,
truncateChildren,
}

接下来有个canUpdate 很关键:

1
2
3
4
5
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}

这个方法表示什么情况下视图可以更新,只有当 oldWidget.runtimeType == newWidget.runtimeTypeoldWidget.key == newWidget.key的情况下,widget方可更新(一个widget可以更新的标准是runtimeTypekey都相同)。

同时,当在setState的本质是调用Element类的markNeedsBuild实现的。

Widget会继承自DiagnosticableTree,而DiagnosticableTree会混入Diagnosticable

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
abstract class DiagnosticableTree with Diagnosticable {
const DiagnosticableTree();

String toStringShallow({
String joiner = ', ',
DiagnosticLevel minLevel = DiagnosticLevel.debug,
}) {
String? shallowString;
assert(() {
final StringBuffer result = StringBuffer();
result.write(toString());
result.write(joiner);
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
debugFillProperties(builder);
result.write(
builder.properties.where((DiagnosticsNode n) => !n.isFiltered(minLevel))
.join(joiner),
);
shallowString = result.toString();
return true;
}());
return shallowString ?? toString();
}

String toStringDeep({
String prefixLineOne = '',
String? prefixOtherLines,
DiagnosticLevel minLevel = DiagnosticLevel.debug,
}) {
return toDiagnosticsNode().toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines, minLevel: minLevel);
}

@override
String toStringShort() => describeIdentity(this);

@override
DiagnosticsNode toDiagnosticsNode({ String? name, DiagnosticsTreeStyle? style }) {
return DiagnosticableTreeNode(
name: name,
value: this,
style: style,
);
}

@protected
List<DiagnosticsNode> debugDescribeChildren() => const <DiagnosticsNode>[];
}

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
mixin Diagnosticable {

String toStringShort() => describeIdentity(this);

@override
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
String? fullString;
assert(() {
fullString = toDiagnosticsNode(style: DiagnosticsTreeStyle.singleLine).toString(minLevel: minLevel);
return true;
}());
return fullString ?? toStringShort();
}

DiagnosticsNode toDiagnosticsNode({ String? name, DiagnosticsTreeStyle? style }) {
return DiagnosticableNode<Diagnosticable>(
name: name,
value: this,
style: style,
);
}

@protected
@mustCallSuper
void debugFillProperties(DiagnosticPropertiesBuilder properties) { }
}

Widget是继承于DiagnosticableTree 的,关于DiagnosticableTree 这个类,它主要用于在调试时获取子类的各种属性和children信息,在flutter各个对象中你经常能看到它,目前我们不需要去关心与之相关的内容。

我们可以看到,Widget 是一个抽象类;同时它被immutable 注解修饰,说明它的各个属性一定是不可变的,这就是为什么我们写各种Widget 时,所写的各个属性要加 final 的原因,否则编译器就会发出警告。

参考文献: 从源码看flutter(一):Widget篇
参考文献: abstract