flutter2_widget_1简介

参考

Flutter 是如何渲染的? (juejin.cn)

从架构到源码:一文了解Flutter渲染机制_阿里技术-CSDN博客

 

Widget 树,Element 树和 RenderObject 关系

Flutter 中有三棵树:Widget 树,Element 树和 RenderObject 树。

  • Widget Tree:为Element描述需要的配置,调用createElement方法创建Element,决定Element是否需要更新。Flutter通过查分算法比对Widget树前后的变化,来决定Element的State是否改变。
  • Element Tree:表示Widget Tree特定位置的一个实例,调用createRenderObject创建RenderObject,同时持有Widget和RenderObject,负责管理Widget的配置和RenderObjec的渲染。Element的状态由Flutter维护,开发人员只需要维护Widget即可。
  • RenderObject Tree:RenderObject绘制,测量和绘制节点,布局子节点,处理输入事件。

当应用启动时 Flutter 从main()方法中的runApp()开始,从上到下创建Widget,

创建widget的同时会调用 Widget 上的 createElement() 方法创建每个 Element 对象,形成 Element Tree。

最后调用 Element 的 createRenderObject() 方法(当然不是每个widget的element都有renderObject)创建每个渲染对象,形成一个 Render Tree。

widget

上边说了Widget只是element的配置对象。

abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });

  finaKey key;

  @protected
  @factory
  Element createElement();

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

 

在widget的构造函数中都有一个可选参数key,这个key到底有啥用呢,

canUpdate 方法通过比较 新widget和 旧widget的 runtimeType 和 key 属性是否相同来决定 是用新的widget配置Element,还是重新创建Element再把新widget配置给它。

widget有两个主要的子类StatelessWidget和StatefulWidget,它们的主要区别是子widget是否可以被重建,StatefulWidget可以,StatelessWidget不可以。

StatelessWidget

abstract class StatelessWidget extends Widget {
  const StatelessWidget({ Key key }) : super(key: key);

  @override
  StatelessElement createElement() => StatelessElement(this);

  @protected
  Widget build(BuildContext context);
}
  • 比widget多了一个build方法,
  • createElement()方法返回的是StatelessElement对象,注意把自身传递进element中了。

 

StatefulWidget

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);

  @override
  StatefulElement createElement() => StatefulElement(this);

  @protected
  @factory
  State createState();
}
  • 比widget多了一个createState方法,
  • createElement()方法返回的是StatefulElement对象,注意把自身传递进element中了。

Element

wps1

Element 用于管理应用 UI 的更新和更改,管理部件的生命周期,每个 Element 都包含对 Widget 和 RenderObject 的引用。

当子Widget 变化时,如果新旧两个子 Widget 的 runtimeType 和 key 属性相同的,那么子Element 会通过 Element.update() 更新widget配置,否则旧的 子Element 会被删除,新生成的子Element 插入到树中。

abstract class Element extends DiagnosticableTree implements BuildContext {
  /// Creates an element that uses the given widget as its configuration.
  ///
  /// Typically called by an override of [Widget.createElement].
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

  Element _parent;

  /// Information set by parent to define where this child fits in its parent's
  /// child list.
  ///
  /// Subclasses of Element that only have one child should use nulfor
  /// the slot for that child.
  dynamic get slot => _slot;
  dynamic _slot;

  /// An integer that is guaranteed to be greater than the parent's, if any.
  /// The element at the root of the tree must have a depth greater than 0.
  int get depth => _depth;
  int _depth;

  /// The configuration for this element.
  @override
  Widget get widget => _widget;
  Widget _widget;

  /// The object that manages the lifecycle of this element.
  @override
  BuildOwner get owner => _owner;
  BuildOwner _owner;

  boo_active = false;

  /// The render object at (or below) this location in the tree.
  ///
  /// If this object is a [RenderObjectElement], the render object is the one at
  /// this location in the tree. Otherwise, this getter wilwalk down the tree
  /// untiit finds a [RenderObjectElement].
  RenderObject get renderObject {
    RenderObject result;
    void visit(Element element) {
      assert(result == null); // this verifies that there's only one child
      if (element is RenderObjectElement)
        result = element.renderObject;
      else
        element.visitChildren(visit);
    }
    visit(this);
    return result;
  }

这个方法就是用来更新 子element的widget配置的地方,会用到 Widget.canUpdate来决定是否复用 子element。
  @protected
  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    if (newWidget == null) {
      if (child != null)
        deactivateChild(child);
      return null;
    }
    Element newChild;
    if (child != null) {
      boohasSameSuperclass = true;
      assert(() {
        finaint oldElementClass = Element._debugConcreteSubtype(child);
        finaint newWidgetClass = Widget._debugConcreteSubtype(newWidget);
        hasSameSuperclass = oldElementClass == newWidgetClass;
        return true;
      }());
      if (hasSameSuperclass && child.widget == newWidget) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        newChild = child;

      } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        child.update(newWidget);
        newChild = child;

      } else {
        deactivateChild(child);
        assert(child._parent == null);
        newChild = inflateWidget(newWidget, newSlot);
      }
    } else {
      newChild = inflateWidget(newWidget, newSlot);
    }

    return newChild;
  }

此方法就是挂载到element树上
  @mustCallSuper
  void mount(Element parent, dynamic newSlot) {
    _parent = parent;
    _slot = newSlot;
    _depth = _parent != nul? _parent.depth + 1 : 1;
    _active = true;
    if (parent != null) // Only assign ownership if the parent is non-null
      _owner = parent.owner;
    finaKey key = widget.key;
    if (key is GlobalKey) {
      key._register(this);
    }
    _updateInheritance();
  }

  @mustCallSuper
  void update(covariant Widget newWidget) {
    _widget = newWidget;
  }

  void detachRenderObject() {
    visitChildren((Element child) {
      child.detachRenderObject();
    });
    _slot = null;
  }

  void attachRenderObject(dynamic newSlot) {
    assert(_slot == null);
    visitChildren((Element child) {
      child.attachRenderObject(newSlot);
    });
    _slot = newSlot;
  }

此方法就是当 子element需要重建时调用此方法,创建子element后会挂载到element树上。
  @protected
  Element inflateWidget(Widget newWidget, dynamic newSlot) {
    assert(newWidget != null);
    finaKey key = newWidget.key;
    if (key is GlobalKey) {
      finaElement newChild = _retakeInactiveElement(key, newWidget);
      if (newChild != null) {
        newChild._activateWithParent(this, newSlot);
        finaElement updatedChild = updateChild(newChild, newWidget, newSlot);
        assert(newChild == updatedChild);
        return updatedChild;
      }
    }
    finaElement newChild = newWidget.createElement();

    newChild.mount(this, newSlot);
    assert(newChild._debugLifecycleState == _ElementLifecycle.active);
    return newChild;
  }
 
把child从element树上移除。
  @protected
  void deactivateChild(Element child) {
    child._parent = null;
    child.detachRenderObject();
    owner._inactiveElements.add(child); // this eventually calls child.deactivate()
  }

  @mustCallSuper
  void activate() {
    finaboohadDependencies = (_dependencies != nul&& _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies;
    _active = true;
    // We unregistered our dependencies in deactivate, but never cleared the list.
    // Since we're going to be reused, let's clear our list now.
    _dependencies?.clear();
    _hadUnsatisfiedDependencies = false;
    _updateInheritance();
    assert(() {
      _debugLifecycleState = _ElementLifecycle.active;
      return true;
    }());
    if (_dirty)
      owner.scheduleBuildFor(this);
    if (hadDependencies)
      didChangeDependencies();
  }

  @mustCallSuper
  void deactivate() {
    if (_dependencies != nul&& _dependencies.isNotEmpty) {
      for (finaInheritedElement dependency in _dependencies)
        dependency._dependents.remove(this);
      // For expediency, we don't actually clear the list here, even though it's
      // no longer representative of what we are registered with. If we never
      // get re-used, it doesn't matter. If we do, then we'lclear the list in
      // activate(). The benefit of this is that it allows Element's activate()
      // implementation to decide whether to rebuild based on whether we had
      // dependencies here.
    }
    _inheritedWidgets = null;
    _active = false;
  }


  @mustCallSuper
  void unmount() {
    // Use the private property to avoid a CastError during hot reload.
    finaKey key = _widget.key;
    if (key is GlobalKey) {
      key._unregister(this);
    }
  }

  @override
  RenderObject findRenderObject() => renderObject;

  @mustCallSuper
  void didChangeDependencies() {
    assert(_active); // otherwise markNeedsBuild is a no-op
    assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
    markNeedsBuild();
  }

  /// Returns true if the element has been marked as needing rebuilding.
  booget dirty => _dirty;
  boo_dirty = true;

  // Whether this is in owner._dirtyElements. This is used to know whether we
  // should be adding the element back into the list when it's reactivated.
  boo_inDirtyList = false;

标记此element为dirty,在下一帧时会重建widget。
  void markNeedsBuild() {
    assert(_debugLifecycleState != _ElementLifecycle.defunct);
    if (!_active)
      return;

    if (dirty)
      return;
    _dirty = true;
    owner.scheduleBuildFor(this);
  }

  /// Called by the [BuildOwner] when [BuildOwner.scheduleBuildFor] has been
  /// called to mark this element dirty, by [mount] when the element is first
  /// built, and by [update] when the widget has changed.
  void rebuild() {
    assert(_debugLifecycleState != _ElementLifecycle.initial);
    if (!_active || !_dirty)
      return;

    performRebuild();

  }

  /// Called by rebuild() after the appropriate checks have been made.
  @protected
  void performRebuild();
}

 

ComponentElement

abstract class ComponentElement extends Element {
  /// Creates an element that uses the given widget as its configuration.
  ComponentElement(Widget widget) : super(widget);

  Element _child;

  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);

    _firstBuild();
  }

  void _firstBuild() {
    rebuild();
  }

  /// Calls the [StatelessWidget.build] method of the [StatelessWidget] object
  /// (for stateless widgets) or the [State.build] method of the [State] object
  /// (for statefuwidgets) and then updates the widget tree.
  ///
  /// Called automatically during [mount] to generate the first build, and by
  /// [rebuild] when the element needs updating.
  @override
  void performRebuild() {
    Widget built;
    try {
      built = build();
    } catch (e, stack) {
    } finally {
      // We delay marking the element as clean untiafter calling build() so
      // that attempts to markNeedsBuild() during build() wilbe ignored.
      _dirty = false;
    }
    try {
      _child = updateChild(_child, built, slot);
      assert(_child != null);
    } catch (e, stack) {
      _child = updateChild(null, built, slot);
    }

    if (!kReleaseMode && debugProfileBuildsEnabled)
      Timeline.finishSync();
  }

  /// Subclasses should override this function to actually calthe appropriate
  /// `build` function (e.g., [StatelessWidget.build] or [State.build]) for
  /// their widget.
  @protected
  Widget build();
}

 

StatelessElement

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);
    assert(widget == newWidget);
    _dirty = true;
    rebuild();
  }
}

flutter内部是通过调用element的build方法来去创建子widget

StatefulElement

class StatefulElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {

    assert(_state._element == null);
    _state._element = this;

    _state._widget = widget;
    assert(_state._debugLifecycleState == _StateLifecycle.created);
  }

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

  /// The [State] instance associated with this location in the tree.
  ///
  /// There is a one-to-one relationship between [State] objects and the
  /// [StatefulElement] objects that hold them. The [State] objects are created
  /// by [StatefulElement] in [mount].
  State<StatefulWidget> get state => _state;
  State<StatefulWidget> _state;

  @override
  void reassemble() {
    state.reassemble();
    super.reassemble();
  }

  @override
  void _firstBuild() {
    assert(_state._debugLifecycleState == _StateLifecycle.created);
    try {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
      finadynamic debugCheckForReturnedFuture = _state.initState() as dynamic;

    } finally {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
    }
    assert(() {
      _state._debugLifecycleState = _StateLifecycle.initialized;
      return true;
    }());
    _state.didChangeDependencies();
    assert(() {
      _state._debugLifecycleState = _StateLifecycle.ready;
      return true;
    }());
    super._firstBuild();
  }

  @override
  void performRebuild() {
    if (_didChangeDependencies) {
      _state.didChangeDependencies();
      _didChangeDependencies = false;
    }
    super.performRebuild();
  }

@override
void update(StatefulWidget newWidget) {
  super.update(newWidget);
  assert(widget == newWidget);
  finaStatefulWidget oldWidget = _state._widget;

  _dirty = true;
  _state._widget = widget as StatefulWidget;
  try {
    _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
    finadynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic;
  } finally {
    _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
  }
  rebuild();
}


  @override
  void activate() {
    super.activate();

    assert(_active); // otherwise markNeedsBuild is a no-op
    markNeedsBuild();
  }

  @override
  void deactivate() {
    _state.deactivate();
    super.deactivate();
  }

  @override
  void unmount() {
    super.unmount();
    _state.dispose();

    _state._element = null;
    _state = null;
  }


  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _didChangeDependencies = true;
  }
}

 

State

是StatefulWidget的createState创建的一个对象,之所以放到这里说,是因为State的周期方法都是在StatefulElement中调用的。

abstract class State<T extends StatefulWidget> with Diagnosticable {

  T get widget => _widget;
  T _widget;

  BuildContext get context => _element;
  StatefulElement _element;

  booget mounted => _element != null;

  @protected
  @mustCallSuper
  void initState() {
    assert(_debugLifecycleState == _StateLifecycle.created);
  }

  @mustCallSuper
  @protected
  void didUpdateWidget(covariant T oldWidget) { }

  @protected
  void setState(VoidCallback fn) {
    finadynamic result = fn() as dynamic;
    _element.markNeedsBuild();
  }

  @protected
  @mustCallSuper
  void deactivate() { }

  @protected
  @mustCallSuper
  void dispose() {
    assert(_debugLifecycleState == _StateLifecycle.ready);
    assert(() {
      _debugLifecycleState = _StateLifecycle.defunct;
      return true;
    }());
  }

  @protected
  Widget build(BuildContext context);

  @protected
  @mustCallSuper
  void didChangeDependencies() { }
}

state对象持有了element和widget。

  • StatefulWidget.createState()

当StatefulElement创建时,会调用此方法创建state对象。

  • State.initState()

一般用来做一些初始化操作,此方法是在StatefulElement.firstBuild时调用。

  • state.didChangeDependencies()

当State对象的依赖发生变化时会被调用;例如:在之前build() 中包含了一个InheritedWidget,然后在之后的build() 中InheritedWidget发生了变化,那么此时InheritedWidget的子widget的didChangeDependencies()回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时,Flutter framework会通知widget调用此回调。

  • state.didUpdateWidget(oldWidget)

当widget需要更新时调用,

  • state.deactivate()

当State对象从树中被移除时,会调用此回调。在一些场景下,Flutter framework会将State对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。

  • State.dispose()

当element卸载时会调用此方法,也就是说此时需要做一些清理工作,因为此state已经不需要了,可能会重建一个state。

  • State.setState()

此方法是用来重建子widget。

void setState(VoidCallback fn) {
  finadynamic result = fn() as dynamic;
  _element.markNeedsBuild();
}

调用此方法后会去调用state的build方法,来去重建子widget。

生命周期流程图

wps2

 

 

 

为什么需要三棵树

使用三棵树的目的是尽可能复用 Element。

复用 Element 对性能非常重要,因为 Element 拥有两份关键数据:Statefuwidget 的状态对象及底层的RenderObject。

当应用的结构很简单时,或许体现不出这种优势,一旦应用复杂起来,构成页面的元素越来越多,重新创建 3 棵树的代价是很高的,所以需要最小化更新操作。

当 Flutter 能够复用 Element 时,用户界面的逻辑状态信息是不变的,并且可以重用之前计算的布局信息,避免遍历整棵树。

 

BuildContext

BuildContext class - widgets library - Dart API (flutter-io.cn)

继承关系如下:

wps3

widget树中一个widget位置的句柄。

这个类提供了一组方法,可以被StatelessWidget.build和State对象的方法使用。

每个widget都有其自己的BuildContext,它将成为StatelessWidget.build或State.build函数返回的widget的父级。同样,RenderObjectWidget的任何children的父级。

abstract class BuildContext {
  /// The current configuration of the [Element] that is this [BuildContext].
  Widget get widget;

  /// The [BuildOwner] for this context. The [BuildOwner] is in charge of
  /// managing the rendering pipeline for this context.
  BuildOwner get owner;

  /// 如果当前widget不是RenderObjectWidget则会向下去找到一个最近的子RenderObjectWidget的RenderObject,
  /// 此方法只有在build完成后调用才会返回有效的结果,所以一般会在绘制或手势监听中调用。
  /// 通常此方法会返回一个RenderBox,那么它的大小会可以通过下边的size属性获取到。
  ///
  /// 从理论上讲,调用这个方法的代价比较大(树的深度为O(N)),但在实践中通常是很便宜的,因为树通常有很多RenderObject,因此到最近RenderObject的距离通常很短。
  RenderObject findRenderObject();

  /// 此值只有在findRenderObject()方法返回的是RenderBox时才有值,否则测试模式下会抛出异常,release下返回null。
  /// 和findRenderObject()一样,只能在build之后获取才有效。
  Size get size;

  /// 将此BuildContext注册到ancestor上,这样当ancestor的widget更改时,就会重新构建此BuildContext。
  /// 返回值是`ancestor.widget`.
  ///
  /// 很少直接调用此方法。大多数应用程序应该使用dependOnInheritedWidgetOfExactType,它会在找到适当的InheritedElement祖先之后调用这个方法。
  /// 关于何时调用dependOnInheritedWidgetOfExactType的所有条件也适用于此方法。
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });

  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object aspect });

  InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();

  /// 向上查找是T最近的祖先,不包含自己.
  /// 一般来说,dependOnInheritedWidgetOfExactType更有用,因为inherited widgets在更改时将触发使用者rebuild 。
这种方法适用于在交互事件处理程序(例如手势回调)中使用,或者执行一次性任务,比如断言您有或没有一个特定类型的widget 作为祖先。
widget 的build方法的返回值 不应该依赖于该方法返回的值,因为如果该方法的返回值发生更改,构建上下文将不会重新构建。这可能导致在build方法中使用的数据发生更改,但没有重新构建widget 。

  /// 调用此方法的代价相对较高(树的深度为O(N))。只有在知道从这个小部件到期望的祖先的距离很小并且有边界的情况下才调用这个方法。

  /// 不应该从State.deactivate或State.dispose调用此方法,因为此时widget树不再稳定。要从这些方法中引用一个祖先,请通过在State.didChangeDependencies中调用findAncestorWidgetOfExactType来保存对该祖先的引用。
  T findAncestorWidgetOfExactType<T extends Widget>();

  T findAncestorStateOfType<T extends State>();

  /// 向上找最远的是T类型的祖先State,不包含自己。
  T findRootAncestorStateOfType<T extends State>();

  T findAncestorRenderObjectOfType<T extends RenderObject>();

  /// 传递一个函数类型参数,接收祖先element,直到返回false
  void visitAncestorElements(boovisitor(Element element));

  /// 访问直接子element,ElementVisitor 是一个函数类型,void Function(Element element)
  void visitChildElements(ElementVisitor visitor);
}

 

GlobalKey有什么用

abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
  /// Creates a [LabeledGlobalKey], which is a [GlobalKey] with a labeused for
  /// debugging.
  ///
  /// The labeis purely for debugging and not used for comparing the identity
  /// of the key.
  factory GlobalKey({ String debugLabe}) => LabeledGlobalKey<T>(debugLabel);

  Element get _currentElement => _registry[this];

  /// The build context in which the widget with this key builds.
  ///
  /// The current context is nulif there is no widget in the tree that matches
  /// this globakey.
  BuildContext get currentContext => _currentElement;

  /// The widget in the tree that currently has this globakey.
  ///
  /// The current widget is nulif there is no widget in the tree that matches
  /// this globakey.
  Widget get currentWidget => _currentElement?.widget;

  /// The [State] for the widget in the tree that currently has this globakey.
  ///
  /// The current state is nulif (1) there is no widget in the tree that
  /// matches this globakey, (2) that widget is not a [StatefulWidget], or the
  /// associated [State] object is not a subtype of `T`.
  T get currentState {
    finaElement element = _currentElement;
    if (element is StatefulElement) {
      finaStatefulElement statefulElement = element;
      finaState state = statefulElement.state;
      if (state is T)
        return state;
    }
    return null;
  }
}

GlobalKey是app中全局唯一的,是不能出现两个相同的GlobalKe的widget。

 

GlobalKey是Flutter提供的一种在整个APP中引用element的机制。

如果一个widget设置了GlobalKey,

  • 那么我们便可以通过globalKey.currentWidget获得该widget对象、
  • globalKey.currentElement来获得widget对应的element对象,
  • 如果当前widget是StatefulWidget,则可以通过globalKey.currentState来获得该widget对应的state对象。

 

注意:使用GlobalKey开销较大,如果有其他可选方案,应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的,不能重复。

posted @ 2020-12-07 15:56  嘤嘤嘤123  阅读(353)  评论(0编辑  收藏  举报