Flutter|UI绘制解析

2024/9/20 Flutter知识点

# Flutter UI绘制解析

# Widget、Element、RenderObject概念

因为WidgetTree会大量的重绘,所以Widget必然是廉价的。

Flutter UI有三大元素:Widget、Element、RenderObject。对应这三者也有三个owner负责管理他们,分别是WidgetOwner、BuildOwner、PipelineOwner。

  • Widget 并不是真正的渲染对象,它只是一些廉价的纯对象,持有一些渲染需要的配置信息,它随时不断的被替换着。
  • RenderObject 是真正渲染对象,渲染引擎会根据RenderObject来进行真正的绘制,它是相对稳定且昂贵的。
  • Element 使得不断变化的Widget转变为相对稳定的RenderObject的中间者。

WidgetOwner在不断改变widget,然后向BuildOwner输送一个个WidgetTree,首次的WidgetTree会生成一个与之对应的ElementTree,并生成对应的RenderObjectTree。

BuildOwner每次收到新的WidgetTree就与上一次的进行对比,在ElementTree上只更新变化的部分,Element有可能仅是update一下,也有可能会被替换,Element被替换之后,与之对应的RenderObject也就被替换了。虽然WidgetTree全部被替换了,但ElementTree和RenderObjectTree只替换了变化的部分。

PipelineOwner管理着真正需要绘制的View。它会对RenderObjectTree中发生变化节点的进行flush操作,最后交给底层引擎渲染。

# Widget、Element、RenderObject联系

Widget赋予了所有的Widget子类三个关键的能力:保证自身唯一以及定位的Key、创建Element的createElement、是否能更新的canUpdate。

Widget子类有RenderObjectWidget,它代表着有渲染能力,拥有一个创建RenderObject的createRenderObject方法。

不难看出,Widget、Element、RenderObject的创建关系并不是线性传递的,Element和RenderObject都是Widget创建出来的,也并不是每一个Widget都有与之对应的RenderObjectWidget。这也解释图中RenderObjectTree看起来和前面的WidgetTree缺少了一些节点。

# Widget、Element、RenderObject 的第一次创建与关联

Flutter的WidgetTree很简单,只有最底层的RootWidget。

  • RootWidget
  • MyApp (自定义)
  • MyMaterialApp (自定义)
void runApp(Widget app) {
  final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
  ...
  binding
    ..scheduleAttachRootWidget(binding.wrapWithDefaultView(app))
    ..scheduleWarmUpFrame();
}
1
2
3
4
5
6
7

WidgetsFlutterBinding mixin 了一系列的Binding,这些Binding持有了上面说的一些owner,比如BuildOwner、PipelineOwner,所以随着WidgetsFlutterBinding的初始化,其他的Binding也被初始化了,此时Flutter启动!

  
  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
  }

  void attachRootWidget(Widget rootWidget) {
    attachToBuildOwner(RootWidget(
      debugShortDescription: '[root]',
      child: rootWidget,
    ));
  }

  void attachToBuildOwner(RootWidget widget) {
    final bool isBootstrapFrame = rootElement == null;
    _readyToProduceFrames = true;
    _rootElement = widget.attach(buildOwner!, rootElement as RootElement?);
    if (isBootstrapFrame) {
      SchedulerBinding.instance.ensureVisualUpdate();
    }
  }

  RootElement attach(BuildOwner owner, [ RootElement? element ]) {
    if (element == null) {
      owner.lockState(() {
        element = createElement(); // 创建rootElement
        assert(element != null);
        element!.assignOwner(owner); // 绑定BuildOwner
      });
      owner.buildScope(element!, () { //子widget的初始化从这里开始
        element!.mount(/* parent */ null, /* slot */ null); // 初始化子Widget前,先执行rootElement的mount方法
      });
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element!;
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  1. binding.wrapWithDefaultView(app)方法创建了Root[Widget]
  2. scheduleAttachRootWidget中的attachRootWidget方法调用widget.attach创建了Root[Element](RootElement)
  3. 使用element!.assignOwner(owner),绑定了BuildOwner
  4. Root[Element]尝试调用mount方法将自己挂载到父Element上,因为自己就是root了,所以没有父Element,挂空
  5. mount的过程中会调用Widget.createRenderObject创建了Root[RenderObject]
    • 代码实现

          class RootElement extends Element with RootElementMixin {
              ...
              
              void mount(Element? parent, Object? newSlot) {
                  assert(parent == null); // We are the root!
                  super.mount(parent, newSlot);
                  _rebuild();
                  assert(_child != null);
                  super.performRebuild(); // clears the "dirty" flag
              }
              ...
          }
      
          mixin RootElementMixin on Element {
              ...
              
              void mount(Element? parent, Object? newSlot) {
                  // Root elements should never have parents.
                  assert(parent == null);
                  assert(newSlot == null);
                  super.mount(parent, newSlot);
              }
              ...
          }
      
          abstract class RenderObjectElement extends Element {
              
              void mount(Element? parent, Object? newSlot) {
                  super.mount(parent, newSlot);
                  ...
                  _renderObject = (widget as RenderObjectWidget).createRenderObject(this);
                  ...
                  attachRenderObject(newSlot);
                  super.performRebuild(); // clears the "dirty" flag
              }
          }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36

      它的child,也就是我们传入的app是怎么挂载父控件上的呢?

  6. 我们将app作为参数传给了Root[Widget]app[Widget]也就成了为Root[Widget]child[Widget]
  7. 调用owner.buildScope,开始执行子Tree的创建以及挂载!此时root节点会先调用创建root
  8. 调用createElement方法创建出Child[Element]
  9. 调用Elementmount方法,将自己挂载到Root[Element]上,形成一棵树
  10. 挂载的同时,调用widget.createRenderObject,创建Child[RenderObject]
  11. 创建完成后,调用attachRenderObject,完成和Root[RenderObject]的链接

就这样,WidgetTree、ElementTree、RenderObject创建完成,并有各自的链接关系。

# Flutter的刷新流程:Element的复用

虽然createRenderObject方法的实现是在Widget当中,但持有RenderObject引用的却是RenderObjectElement。

abstract class RenderObjectElement extends Element {
 ...
 
  
  RenderObjectWidget get widget => super.widget;

  
  RenderObject get renderObject => _renderObject;
  RenderObject _renderObject;
}
1
2
3
4
5
6
7
8
9
10

Element同时持有两者。Root Widget,Root Element,Root RenderObject都已经创建完成并且三者链接成功。

# 刷新机制

通知底层渲染

setState -> _element.markNeedsBuild() -> owner.scheduleBuildFor(this) -> _dirtyElements.add(element) -> onBuildScheduled!() -> _handleBuildScheduled -> ensureVisualUpdate -> scheduleFrame -> ensureFrameCallbacksRegistered - _handleDrawFrame - handleDrawFrame -> _invokeFrameCallback(callback, _currentFrameTimeStamp!) ->_persistentCallbacks -> addPersistentFrameCallback -> addPersistentFrameCallback(_handlePersistentFrameCallback); -> drawFrame()

底层回调dart层

void buildScope(Element context, [VoidCallback callback]) {
    ...
    try {
    ...
      //1.按照Element的深度从小到大,对_dirtyElements进行排序
      _dirtyElements.sort(Element._sort);
      ...
      int dirtyCount = _dirtyElements.length;
      int index = 0;
      while (index < dirtyCount) {
        try {
         //2.遍历rebuild
          _dirtyElements[index].rebuild();
        } catch (e, stack) {
        }
        index += 1;
      }
    } finally {
      for (Element element in _dirtyElements) {
        element._inDirtyList = false;
      }
      //3.清空
      _dirtyElements.clear();
    ...
    }
  }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Last Updated: 2024/11/22 17:43:46