Bf3bfe9bd52db77bd032e6d3469af302
Flutter 核心原理:Widget、Element和RenderObject是如何协同工作的

谈到Flutter的渲染,就离不开Widget、Element、RenderObject这三个概念。在Flutter中和大家接触最多的就是Widget,对于Widget很容易理解,可以看作是组件或控件的概念,那么Element和RenderObject是什么,他们具体干什么的,初学者就会有些难以理解。这篇文章就介绍一下Widget、Element和RenderObject这三个概念,并且说明他们是如何协同工作的。

首先我们来看一段简单Demo,这段代码包含两个Widget:


void main() => runApp(DemoWidget());

class DemoWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
    );
  }
}

现在我们来看看Flutter是如何开始渲染的。我们相当于以这种声明的形式创建了一棵包含2个Widget(DemoWidget和Padding)的Widget树提供给了Flutter。当运行的时候,Flutter会创建一个RootWidget,把我们定义的Widget树加入其中,这样就生成了一棵完整的Widget树。Flutter有了这棵Widget树是不是就可以直接去渲染了呢,并不是这样的。

接下来,我们的Element就要闪亮登场了,RootWidget会通过调用著名的Widget.createElement创建一个RootElement,同时这个RootElement拥有了RootWidget也就拥有了整个Widget树。


// RenderObjectToWidgetAdapter类的部分源码
// RenderObjectToWidgetAdapter就是RootWidget。
// RenderObjectToWidgetElement就是RootElement。
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
    ...
    @override
    RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
    ...
}

RootElement会递归遍历整个Widget树。通过调用每个Widget的createElement方法创建相应的Element,按Widget树的结构连接起来,就这样生成了Element树。

在RootElement遍历Widget树创建Element同时,Element也会调用Widget的createRenderObject创建RenderObject,同时也按树的结构连接起来,这样,Flutter又得到了第三棵树也就是RenderObject树。而RootRenderObject就是RootWidget的createRenderObject方法返回的RenderObject。


// Padding类的部分源码
// 通过createRenderObject获得RenderObject。
class Padding extends SingleChildRenderObjectWidget {    
  ..
  @override
  RenderPadding createRenderObject(BuildContext context) {
    return RenderPadding(
      padding: padding,
      textDirection: Directionality.of(context),
    );
  }
  ..
}

RenderObject实现了基础的layout和绘制协议,一些RenderObject子类则实现了子节点模型和定义坐标系统。实际上只有RenderObject树,参与了最后的渲染绘制。也就是不管是Widget树还是Element树最终的目的就是为了生成RenderObject树来进行渲染。



// RendererBinding类的部分源码
@protected
void drawFrame() {
...
// renderView 就是RootRenderObject
renderView.compositeFrame(); // 发送给GPU

top Created with Sketch.