Flutter 的Widget

666 阅读3分钟

「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」。

在学习Flutter的时候,我们经常听到或者学习到一句话:万物皆是widget。那么什么是widget呢?

Widget

image.png

首先widget一個抽象类,它继承自DiagnosticableTree。

abstract class Widget extends DiagnosticableTree

它主要用于在调试时提供调试信息。

Key

final Key? key;

它是 Element 去识别 Widget 的一个重要标识。Key如果未指定,则为默认值nullkey主要的作用是决定是否在下一次build时复用旧的 widget,决定的条件在canUpdate()方法中。

createElement

@protected
  @factory
  Element createElement();

在构建widget时,会先调用此方法生成对应节点的Element对象。此方法是隐式调用的,在实际开发过程中基本不会用到。通过这里我们可以知道widget创建一个element 元素,也从这里往源码里面挖掘会牵扯出WidgetElement以及RenderObject这三个树,以及他们之间的关系。

toStringShort

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

是对该Widget的简短说明,包括Widget的类型和对应的Key等;

debugFillProperties

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

复写父类DiagnosticableTree的方法,主要是设置诊断树的一些特性。如果您编写自己的widget,则可以通过覆盖debugFillProperties()来添加信息。

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
  super.debugFillProperties(properties);
  properties.add(IntProperty('_counter', _counter));
}

我们使用了IntProperty,更多的调式属性类型请在文档中查看。在实际的调式中我们可以在Flutter DevTools详细信息树视图,它显示有关小部件、渲染对象和状态的有用信息)。在这里就能看到刚才我们新添加的_counter

image.png

canUpdate

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

它主要用于在 Widget 树重新build时复用旧的 widget,如果返回 true 表示可以更新当前 Element 对象,并用新的 widget 更新 Element 的 widget 。返回false 表示不用更新当前Element 对象。

_debugConcreteSubtype

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

_debugConcreteSubtype方法返回一个数值,用于指示Widget的实际子类型,1表示StatefulWidget,2表示StatelessWidget ,其他的用0来表示。

operator == 与 hashCode

  @override
  @nonVirtual
  bool operator ==(Object other) => super == other;
  
  @override
  @nonVirtual
  int get hashCode => super.hashCode;

是当且仅this 对象 和other是同一对象时才返回 true 。但是这里需要注意的是operator ==是被标识为@nonVirtual,当==被当用注释的成员被覆盖时发出提示不能覆盖此方法。这个时候如果我们有这种需求的话,我们可以使用identical。具体的可以看这个issue

operator ==identical 的区别

  • == 通过对象的 operator ==比较是否相等.
  • identical 通过比较两个引用的是否是同一个对象判断是否相等.

hashCode是一个对象的哈希值。

void main() {
  var ep1 = IccClass(10);
  var ep2 = IccClass(10);
  var ep3 = IccClass(20);

  print(ep1.hashCode);
  print(ep2.hashCode);
  print(ep3.hashCode);
  print(ep1 == ep2); 
  print(ep1 == ep3); 
  print(identical(ep1, ep2));
  print(identical(ep1, ep3));
}

class IccClass {
   Int id;
   IccClass(this.id);

  // 步骤1
  // bool operator ==(o) => o is IccClass && o.id == id;

  // 步骤2
  //int get hashCode => id;
}

第一次,不打开步骤1和步骤2,然后运行的结果:

582536759
1000007873
619794939
false
false
false
false

第二次,打开步骤1,不打开步骤2,然后运行的结果:

725718048
507628837
416227163
true
false
false
false

第二次,打开步骤2,不打开步骤1,然后运行的结果:

20
20
20
false
false
false
false

第四次,打开步骤1和步骤2,然后运行的结果:

20
20
20
true
false
false
false