Flutter中Widget的生命周期

725 阅读2分钟

Widget 的简介

先看一下Widget. 从 Flutter的注释来看, 官方对Widget的解释是:Describes the configuration for an [Element], 对于Flutter Framework来说, 并且是一个immutable description , 在后续会关联到Element.

Widget - Element - RenderObject 三棵树的内容后面会整理

有三个重点需要:

  1. Widget 是一个不可变的配置信息
  2. Widget 会关联Element类型
  3. 抽象类, 实际在开发时会继承子类: StatefulWidget, StatelessWidget.
  4. StatefulWidget的可变状态完全依赖于关联的State
abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });
  
  final Key key;
    
  @protected
  Element createElement();
  
  ...
      
  /// Whether the `newWidget` can be used to update an [Element] that currently
  /// has the `oldWidget` as its configuration.
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}

StatelessWidget的生命周期

StatelessWidget作为无状态的Widget, 生命周期比较简单:

  1. const StatelessWidget({ Key? key }) : super(key: key); 构造函数
  2. StatelessElement createElement() => StatelessElement(this); Widget关联的Element,
  3. Widget build(BuildContext context); 说明The framework calls this method when this widget is inserted into the tree
//Element 内部会只有 widget
/// An [Element] that uses a [StatelessWidget] as its configuration.
class StatelessElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
  StatelessElement(StatelessWidget widget) : super(widget);

  /// 使用
  @override
  StatelessWidget get widget => super.widget as StatelessWidget;

  @override
  Widget build() => widget.build(this); 

  @override
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    _dirty = true;
    rebuild();
  }
}

StatefulWidget的生命周期

StatefulWidget是有状态的Widget, 与之关联的State的生命周期如下:

  1. widget: const StatefulWidget({ Key? key }) : super(key: key);
  2. widget: StatefulElement createElement() => StatefulElement(this);
  3. widget: State createState()
  4. state: constructor
  5. state: initState
  6. state: didChangeDependencies (若InheritedWidget.rebuild => child.didChangeDependencies)
  7. state: Widget build(BuildContext context); (setState() => didUpdateWidget => build)
  8. state: 将Widget - Element加入tree
  9. state: deactivate
  10. state: dispose
  11. Element移除true
  1. InheritedWidget 方法 rebuild 时, 会调用childNode 中的 didChangeDependencies
  1. setState() 方法时, 会调用didUpdateWidget 方法, 然后调用build

以上过程中几个关键的内容:

  1. StatefulWidget在调用createElement()方法时, 关键的内容:
// StatefulWidget 中在 Widget被 mount 到 tree 时调用 
StatefulElement createElement() {
    // Element 的构造函数
    return StatefulElement(this);
}
​
/// Creates an element that uses the given widget as its configuration.
/// Element构造函数中
/// 1. 会主动调用 widget.createState()方法, 并创建 _state!!!
/// 2. 然后, 在state中持有 element 和 widget
StatefulElement(StatefulWidget widget): _state = widget.createState(), super(widget) {
    ...
   state._element = this;
   state._widget = widget;
}
  1. createState()时, 可能会调用多次. For example, if the widget is inserted into the tree in multiple locations, the framework will create a separate [State] object for each location

  2. SetState的实现与dirty Element的标记与更新:

    /// State 中的方法
    void setState(VoidCallback fn) {
       // 先调用 fn() ... 
       final Object? result = fn() as dynamic;
       _element!.markNeedsBuild();
    }
    
    /// Element 中的方法
    /// Marks the element as dirty and adds it to the global list of widgets to
    /// rebuild in the next frame.
    ///
    /// Since it is inefficient to build an element twice in one frame,
    /// applications and widgets should be structured so as to only mark
    /// widgets dirty during event handlers before the frame begins, not during
    /// the build itself.
    void markNeedsBuild() {
        ...
        _dirty = true; // 仅仅标记 Element, 会加入到全局 dirty list 中, 下一帧循环时, 会更新
        owner!.scheduleBuildFor(this); // owner 是在调用 mount 时, 传入的parent.owner设置的
    }
    
    // BuildOwner 类 -- Framework 中的管理类
    /// Adds an element to the dirty elements list so that it will be rebuilt
    /// when [WidgetsBinding.drawFrame] calls [buildScope].
    void scheduleBuildFor(Element element) {
        ...
        _dirtyElements.add(element); // 将 element 添加到全局的 dirty list 中
        element._inDirtyList = true;
    }