BuildOwner是负责管理widgets框架的类。

classDiagram

class BuildOwner {
   onBuildScheduled: VoidCallback
   _inactiveElements: _InactiveElements
   _dirtyElements: List<Element>
    scheduleBuildFor() void
}

BuildOwner 一开始是由 WidgetsBindinginitInstances 里构造。

mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;

    assert(() {
      _debugAddStackFilters();
      return true;
    }());

    // Initialization of [_buildOwner] has to be done after
    // [super.initInstances] is called, as it requires [ServicesBinding] to
    // properly setup the [defaultBinaryMessenger] instance.
    _buildOwner = BuildOwner();
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
    platformDispatcher.onLocaleChanged = handleLocaleChanged;
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    SystemChannels.backGesture.setMethodCallHandler(
      _handleBackGestureInvocation,
    );
    assert(() {
      FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
      return true;
    }());
    platformMenuDelegate = DefaultPlatformMenuDelegate();
  }
  .... 
  }

同时会设置BuildOwneronBuildScheduled 的参数值,而这个onBuildScheduled值是_handleBuildScheduled 。他最终调用platformDispatcher.scheduleFrame()向native层申请调度下一帧的渲染。

  void _handleBuildScheduled() {
      ensureVisualUpdate();
  }

  void ensureVisualUpdate() {
    switch (schedulerPhase) {
      case SchedulerPhase.idle:
      case SchedulerPhase.postFrameCallbacks:
        scheduleFrame();
        return;
      case SchedulerPhase.transientCallbacks:
      case SchedulerPhase.midFrameMicrotasks:
      case SchedulerPhase.persistentCallbacks:
        return;
    }
  }

在首帧渲染的时候,会先调用根widget-RootWidgetattach方法来创建RootWidgetElement对象,然后调用assignOwner 方法来完成跟BuildOwner的关联。然后在构建Element树的时候会调用每个子Widgetmount方法来构建Element树。mount的方法会将父的ElementBuildOwner传递给子Element进行关联。这样就给整个Element树都赋予了重绘的能力。

Element如何利用BuildOwner来完成Widget的重绘呢?

StatefulWidgetState调用setState的时候会调用_element!.markNeedsBuild(); 然后 调用BuildOwnerscheduleBuildFor(this);

该方法会将当前Elementdirty 状态置为true,表示当前元素是脏元素需要更新。

然后调用onBuildScheduled 来向native申请调度下一帧渲染,然后将通过将当前这个Element对像添加进BuildOwner对象的_dirtyElements 里。之后native通知渲染的时候会调用WidgetsBindingdrawFrame 方法完成构建。

  void scheduleBuildFor(Element element) {
    if (element._inDirtyList) {
      _dirtyElementsNeedsResorting = true;
      return;
    }
    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
      _scheduledFlushDirtyElements = true;
      **onBuildScheduled**!();
    }
    **_dirtyElements.add(element);**
    element._inDirtyList = true;
  }

绘制DrawFrame

  1. drawFrame方法会调用buildScope 方法,buildScope方法会调用BuildScope方法。BuildScope方法会遍历每个脏元素调用它们的rebuild方法来更新。

  2. 这个行为执行完后,清除标记状态。这个过程发生的过程中很有可能部分Element会被卸载-unmount。卸载的Element会被标记为inactivate状态,然后添加进BuildOwner_inactiveElements 中,这些Element 不会被立马让他成为可能被回收的对象,也就是不会马上调用clear。目的是为了能复用这些Element。

    例如一个页面的一个按钮的一个位置挪到另一个widget里的位置中。那这个按钮的Element 会被卸载,但是这个Element 从Widget树看来只是从一个位置挪到为另一个Widget结点的子树。为了避免了这个元素被重复创建,则添加进_inactiveElements 里,在Element树更新,重绘的时候,由于Widget树结构发生变化,就会有大量的Widget需要进行加载,则会调用inflateWidget ,里面会调用_retakeInactiveElement 来优先尝试能不能从之前被卸载的元素列表来加载该Widget。可以的话就能直接复用了。

  3. 最终构建完后调用BuildOwnerfinalizeTree 方法将_inactiveElements进行清理

sequenceDiagram
drawFrame->>+BuildOwner: buildOwner!.buildScope
BuildOwner->>+Element: 
loop 
    Note right of BuildOwner: for each(elements){elements.build}
    BuildOwner-->Element: 
end  
BuildOwner-->>-drawFrame:  

drawFrame->>+BuildOwner: buildOwner!.finalizeTree
BuildOwner-->>-drawFrame: