一、渲染原理

主要是通过底层源码来分享一下Flutter是怎么渲染的。

1、Widget树

树是一种数据结构,Widget树就是存储Widget的树结构。Widget的功能是“描述一个UI元素的配置数据”,它就是说,Widget其实并不是表示最终绘制在设备屏幕上的显示元素,而它只是描述显示元素的一个配置数据。下面就是Flutter官方Demo的Widget树展示:

image.png

经常需要根据不同的数据来展示不同的UI就导致了Widget树极其不稳定,所以Flutter的引擎在渲染的时候不是直接来渲染Widget树,而是渲染Rander树。Rander树展示:
image.png

2、Rander树

上面说到了Flutter引擎是直接渲染Rander树的,Rander树里面存的是RanderObject。但不是所有的Widget都会变成独立RanderObject,只有继承RenderObjectWidget的Widget才会生成独立RanderObject并且加到Rander树里面,进行独立渲染。RenderObjectWidget的定义如下:

/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
/// which wrap [RenderObject]s, which provide the actual rendering of the
/// application.
abstract class RenderObjectWidget extends Widget {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const RenderObjectWidget({ Key? key }) : super(key: key);

  /// RenderObjectWidgets always inflate to a [RenderObjectElement] subclass.
  @override
  @factory
  RenderObjectElement createElement();

  /// Creates an instance of the [RenderObject] class that this
  /// [RenderObjectWidget] represents, using the configuration described by this
  /// [RenderObjectWidget].
  ///
  /// This method should not do anything with the children of the render object.
  /// That should instead be handled by the method that overrides
  /// [RenderObjectElement.mount] in the object rendered by this object's
  /// [createElement] method. See, for example,
  /// [SingleChildRenderObjectElement.mount].
  @protected
  @factory
  RenderObject createRenderObject(BuildContext context);

  /// Copies the configuration described by this [RenderObjectWidget] to the
  /// given [RenderObject], which will be of the same type as returned by this
  /// object's [createRenderObject].
  ///
  /// This method should not do anything to update the children of the render
  /// object. That should instead be handled by the method that overrides
  /// [RenderObjectElement.update] in the object rendered by this object's
  /// [createElement] method. See, for example,
  /// [SingleChildRenderObjectElement.update].
  @protected
  void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }

  /// A render object previously associated with this widget has been removed
  /// from the tree. The given [RenderObject] will be of the same type as
  /// returned by this object's [createRenderObject].
  @protected
  void didUnmountRenderObject(covariant RenderObject renderObject) { }
}

/// A superclass for RenderObjectWidgets that configure RenderObject subclasses
/// that have no children.
abstract class LeafRenderObjectWidget extends RenderObjectWidget {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const LeafRenderObjectWidget({ Key? key }) : super(key: key);

  @override
  LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
}

RenderObjectWidgetWidget的子类,这里面我们需要关注两个方法:RenderObjectElement createElement()RenderObject createRenderObject(BuildContext context),这两个都是抽象方法,需要子类去实现的。

要看createRenderObject,首先找到哪些Widget是继承RenderObjectWidget,比较常见的有FlowFlexColumnRow等。Row来说看一下其继承关系:

class Row extends Flex{...}
class Flex extends MultiChildRenderObjectWidget {...}
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {...}

Row是继承Flex的,在Row定义里面没有看到createRenderObject的实现,那去Flex里面去找:

image.png

可以找到,在Flex里面把这个方法实现了,所以继承FlexWidget都实现了这个方法。返回一个RenderFlex对象,看一下它的继承关系:

class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexParentData>,
                                        RenderBoxContainerDefaultsMixin<RenderBox, FlexParentData>,
                                        DebugOverflowIndicatorMixin{...}
abstract class RenderBox extends RenderObject {...}

3、Elment树

Element就是WidgetUI树具体位置的一个实例化对象,每生成一个Widget都会对应生成一个Element,看看是不是这样的。
先看Widget的定义:

abstract class Widget extends DiagnosticableTree {
...

/// Inflates this configuration to a concrete instance.
  ///
  /// A given widget can be included in the tree zero or more times. In particular
  /// a given widget can be placed in the tree multiple times. Each time a widget
  /// is placed in the tree, it is inflated into an [Element], which means a
  /// widget that is incorporated into the tree multiple times will be inflated
  /// multiple times.
  @protected
  @factory
  Element createElement();
  
  ...

}

Widget里面定义了一个Element createElement()方法,来看看子类都是怎么实现的。

  • 首先是上面提到的RenderObjectWidget的一个子类MultiChildRenderObjectWidget
 @override
  MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);

它返回的是MultiChildRenderObjectElement这个类型:

class MultiChildRenderObjectElement extends RenderObjectElement {...}
abstract class RenderObjectElement extends Element {...}

从继承关系来看,它也是继承Element的。

  • 然后再看看StatelessWidget,它是继承Widget的:
@override
StatelessElement createElement() => StatelessElement(this);

它返回一个StatelessElement类型:

class StatelessElement extends ComponentElement {...}
abstract class ComponentElement extends Element {...}

从继承关系来看,它也是继承Element的。

  • 最后看看StatefulWidget,它也是是继承Widget的:
  @override
  StatefulElement createElement() => StatefulElement(this);

它返回一个StatefulElement类型:

class StatefulElement extends ComponentElement {...}
abstract class ComponentElement extends Element {...}

从继承关系来看,它也是继承Element的。

从上面的分析来看,确实每一个Widget都实现了createElement(),那是什么时候调的呢?它的调用是Flutter隐试调的,肯定会调的,可以在debug模式打个断点看看。
所以说每一个Widget都会有一个Element与之对应,最后会根据Widget树创建一个Element树。

  • 总结一下,我们可以认为Flutter的UI系统包含三棵树:Widget树、Element树、渲染树。他们的依赖关系是:Element树根据Widget树生成,而渲染树又依赖于Element树,如图所示:


    image.png

4 、mount函数

Widget创建的时候就会创建Element对象然后加入到Element树里面,在第一次加入Element树的时候,就会触发Flutter引擎调一个Element方法——mount(),看一下这个方法的定义:

...
  /// Add this element to the tree in the given slot of the given parent.
  ///
  /// The framework calls this function when a newly created element is added to
  /// the tree for the first time. Use this method to initialize state that
  /// depends on having a parent. State that is independent of the parent can
  /// more easily be initialized in the constructor.
  ///
  /// This method transitions the element from the "initial" lifecycle state to
  /// the "active" lifecycle state.
  ///
  /// Subclasses that override this method are likely to want to also override
  /// [update], [visitChildren], [RenderObjectElement.insertRenderObjectChild],
  /// [RenderObjectElement.moveRenderObjectChild], and
  /// [RenderObjectElement.removeRenderObjectChild].
  @mustCallSuper
  void mount(Element? parent, dynamic newSlot) {
    assert(_lifecycleState == _ElementLifecycle.initial);
    assert(widget != null);
    assert(_parent == null);
    assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);
    assert(slot == null);
    _parent = parent;
    _slot = newSlot;
    _lifecycleState = _ElementLifecycle.active;
    _depth = _parent != null ? _parent!.depth + 1 : 1;
    if (parent != null) // Only assign ownership if the parent is non-null
      _owner = parent.owner;
    final Key? key = widget.key;
    if (key is GlobalKey) {
      key._register(this);
    }
    _updateInheritance();
  }
  ...

这个方法非常重要,上面是Elementmount()的实现,来看看他的子类RenderObjectElementStatelessElementStatefulElementmount()方法都做了哪些事情。

4.1、 RenderObjectElementmount()

...

@override
  void mount(Element? parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    assert(() {
      _debugDoingBuild = true;
      return true;
    }());
    _renderObject = widget.createRenderObject(this);
    assert(() {
      _debugDoingBuild = false;
      return true;
    }());
    assert(() {
      _debugUpdateRenderObjectOwner();
      return true;
    }());
    assert(_slot == newSlot);
    attachRenderObject(newSlot);
    _dirty = false;
  }

...

这里面最要的一个方法就是_renderObject = widget.createRenderObject(this);,它说明了RenderObjectElement创建之后Flutter引擎就会创建renderObject,创建完成之后就会调attachRenderObject(newSlot);方法添加到渲染树中。

4.2、 StatelessElementmount()

StatelessElement类里面并没有看到mount()方法,在找其父类ComponentElementmount()方法:

  @override
  void mount(Element? parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    assert(_child == null);
    assert(_lifecycleState == _ElementLifecycle.active);
    _firstBuild();
    assert(_child != null);
  }

这里面调了_firstBuild();方法:

void _firstBuild() {
    rebuild();
  }

接着往下走:

 /// 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() {
   ...
    performRebuild();
  ...
  }

又调了performRebuild()

 @protected
  void performRebuild();

到这里就点不进去了,我们可以同时按住command+alt+B,就会搜到实现:

image.png

这里选择CompentElementperformRebuild进去就可以看到实现:

  /// Calls the [StatelessWidget.build] method of the [StatelessWidget] object
  /// (for stateless widgets) or the [State.build] method of the [State] object
  /// (for stateful widgets) 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() {
   ...
    built = build();
   ...
  }

这里调了一个build()方法:

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

同样按住command+alt+B,搜实现:

image.png

选择StatelessElement的实现:

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

这里调到了widgetbulid()方法,并且将this做为参数传了出去,这里的this指的就是StatelessElement,那这的widget是什么呢,看一下StatelessElement的定义:

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

StatelessElement的构造方法提供了一个参数widget,并传递给其父类的父类ElementElement通过构造方法:

    Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

赋值给成员变量_widget,保存起来,并提供一个获取的方法widget来获取_widget

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

StatelessElement这个类里面重写了Elementwidget方法:

@override
  StatelessWidget get widget => super.widget as StatelessWidget;

StatelessWidget可以看到:

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

在创建StatelessElement的时候是将this传给了StatelessElement的构造方法,这个this就是StatelessWidget

根据上面的分析StatelessElement里面的:

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

widget就是StatelessWidget,this指的就是StatelessElement

来看Flutter官方的Demo

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

这个build方法的参数context其实就是StatelessElement

4.3、 StatefulElementmount()

StatefulElementStatelessElement一样都没有实现mount()方法,都是在他们父类ComponentElement中实现的,流程是一样的,不一样的是build()方法:

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

StatefulElementbuild方法是调statebulid方法,这个跟StatelessElement不一样,这个state是哪里来的呢,看一下StatefulElement的构造方法实现:

/// Creates an element that uses the given widget as its configuration.
  StatefulElement(StatefulWidget widget)
      : state = widget.createState(),
        super(widget) {
    assert(() {
      if (!state._debugTypesAreRight(widget)) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>'),
          ErrorDescription(
            'The createState function for ${widget.runtimeType} returned a state '
            'of type ${state.runtimeType}, which is not a subtype of '
            'State<${widget.runtimeType}>, violating the contract for createState.'
          ),
        ]);
      }
      return true;
    }());
    assert(state._element == null);
    state._element = this;
    assert(
      state._widget == null,
      'The createState function for $widget returned an old or invalid state '
      'instance: ${state._widget}, which is not null, violating the contract '
      'for createState.',
    );
    state._widget = widget;
    assert(state._debugLifecycleState == _StateLifecycle.created);
  }

这个方法里面有两个重点:

  • state = widget.createState(),在创建StatefulElement的时候,调widgetcreateState()方法创建一个State对象保存在成员变量state里面,这就是为什么StatefulWidget实现createState()
  • state._widget = widget;,将widget保存在state_widget成员变量中,在state里面提供一个获取_widget的方法——widget
abstract class State<T extends StatefulWidget> with Diagnosticable {
   ...
   T get widget => _widget!;
   T? _widget;
   ...
  }

这就是为什么在State里面可以使用widget的原因。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容