美文网首页
2023-03-28 flutter widget 更新7-先有

2023-03-28 flutter widget 更新7-先有

作者: 我是小胡胡123 | 来源:发表于2023-03-28 10:03 被阅读0次

Flutter 页面构建,是先有Element还是先有Widget,是不是有点类似“先有鸡还是先有蛋”的迷惑?

带着这个疑问,打一打断点跟踪下程序源码的执行过程,做了一个简单的记录。

本文秉承多年来的一个写作风格,那就是写的内容不知所云,正如红楼梦中的“满纸荒唐言,一把辛酸泪,都云作者痴,谁解其中味”。

就当作是自己的草稿纸吧。大家别看就是了。

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

继续跟进:

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

1、 attachRootWidget

  void attachRootWidget(Widget rootWidget) {
    final bool isBootstrapFrame = renderViewElement == null;
    _readyToProduceFrames = true;

    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      debugShortDescription: '[root]',
      child: rootWidget,
    ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
    if (isBootstrapFrame) {
      SchedulerBinding.instance.ensureVisualUpdate();
    }
  }

这里创建了一个Widget, 类型为:RenderObjectToWidgetAdapter
这个widget的child 是main里面设置的第一个Widget

image.png

2、attachToRenderTree

通过attachToRenderTree 方法,创建了一个Element
类型为RenderObjectToWidgetElement
这个Element的widget指向 RenderObjectToWidgetAdapter这个widget。

  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
    if (element == null) {
      owner.lockState(() {
        element = createElement();
        assert(element != null);
        element!.assignOwner(owner);
      });
      owner.buildScope(element!, () {
        element!.mount(null, null);
      });
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element!;
  }

Widget祖先:

class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget 
abstract class RenderObjectWidget extends Widget 

Element祖先:

class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement 
abstract class RootRenderObjectElement extends RenderObjectElement 
abstract class RenderObjectElement extends Element 

这么看来,是先有Widget 即:class RenderObjectToWidgetAdapter<T extends RenderObject>

然后才是Element, 即:class RenderObjectToWidgetElement<T extends RenderObject>

我们main函数里面写的第一个根Widget 下面的build创建子widget, 是从Element的mount方法开始的。

image.png

3、接着是所有子widget 创建过程。 使用深度优先遍历递归的方式创建所有子节点

3.1、通过 built = build(); 创建新的widget

Widget build() => (widget as StatelessWidget).build(this);
Widget build() => state.build(this);
Widget build() => (widget as ProxyWidget).child;

void performRebuild() {
    assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true));
    Widget? built;
    try {
      assert(() {
        _debugDoingBuild = true;
        return true;
      }());
      built = build();
      assert(() {
        _debugDoingBuild = false;
        return true;
      }());
      debugWidgetBuilderValue(widget, built);
    } catch (e, stack) {
      _debugDoingBuild = false;
      built = ErrorWidget.builder(
        _debugReportException(
          ErrorDescription('building $this'),
          e,
          stack,
          informationCollector: () => <DiagnosticsNode>[
            if (kDebugMode)
              DiagnosticsDebugCreator(DebugCreator(this)),
          ],
        ),
      );
    } finally {
 
      _dirty = false;
      assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false));
    }
    try {
      _child = updateChild(_child, built, slot);
      assert(_child != null);
    } catch (e, stack) {
      built = ErrorWidget.builder(
        _debugReportException(
          ErrorDescription('building $this'),
          e,
          stack,
          informationCollector: () => <DiagnosticsNode>[
            if (kDebugMode)
              DiagnosticsDebugCreator(DebugCreator(this)),
          ],
        ),
      );
      _child = updateChild(null, built, slot);
    }
  }

3.2、Element.inflateWidget方法中的 final Element newChild = newWidget.createElement(); 创建新的Element

Element inflateWidget(Widget newWidget, Object? newSlot) {
    assert(newWidget != null);
 

    try {
      final Key? key = newWidget.key;
      if (key is GlobalKey) {
        final Element? newChild = _retakeInactiveElement(key, newWidget);
        if (newChild != null) {
          assert(newChild._parent == null);
          assert(() {
            _debugCheckForCycles(newChild);
            return true;
          }());
          newChild._activateWithParent(this, newSlot);
          final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
          assert(newChild == updatedChild);
          return updatedChild!;
        }
      }
      final Element newChild = newWidget.createElement();
      assert(() {
        _debugCheckForCycles(newChild);
        return true;
      }());
      newChild.mount(this, newSlot);
      assert(newChild._lifecycleState == _ElementLifecycle.active);

      return newChild;
    } finally {
 
  }

总结main过程启动如下:
在Flutter中,main函数是整个应用程序的入口,它的执行流程如下:

  • 创建一个WidgetsBinding对象,并将其作为参数传递给runApp()方法。
  • 调用runApp()方法,并将根Widget作为参数传递给它。
  • 创建一个Flutter Engine对象,并将其与Flutter引擎进行连接。
  • 启动Flutter引擎,并将Flutter渲染线程、IO线程等线程启动起来。
  • 创建一个RenderView对象,并将其作为根RenderObject添加到渲染树中。
  • 启动Flutter的消息循环,等待消息的到来。
  • 在执行main函数之前,Flutter会先执行一些底层的初始化操作,例如初始化日志系统、初始化渲染引擎、注册系统服务等。这些操作通常由Flutter引擎的C++部分实现。

执行完main函数之后,Flutter会进入消息循环,等待消息的到来。在消息循环中,Flutter会处理各种事件,例如用户输入事件、定时器事件、系统通知等。如果有新的渲染请求,Flutter会根据需要更新渲染树,并将渲染结果显示出来。

需要注意的是,main函数执行的过程通常是异步的,也就是说,Flutter引擎会在后台启动各种线程和服务,并不会阻塞应用程序的主线程。因此,开发者在编写Flutter应用程序时,需要充分考虑异步操作的情况,以避免应用程序出现卡顿或其他异常行为。

在Flutter中,页面创建渲染过程中,Element树、Widget树和RenderObject树是紧密相连的三个树形结构,它们之间的关系可以概括为:

  • Widget树是Element树的逻辑结构,它由一组Widget对象组成,Widget对象描述了页面上的元素应该长成什么样子。在Widget树中,每个Widget对象都有一个对应的Element对象来表示它在页面上的实例。

  • Element树是渲染树的逻辑结构,它由一组Element对象组成,Element对象负责管理和维护Widget对象在页面上的状态和生命周期。在Element树中,每个Element对象都有一个对应的RenderObject对象来表示它在页面上的可视部分。

  • RenderObject树是页面的渲染树,它由一组RenderObject对象组成,RenderObject对象负责将自己的绘制信息提交给底层的图形引擎进行绘制。在RenderObject树中,每个RenderObject对象都有一个对应的Element对象来表示它在Element树中的关系。

在页面创建渲染过程中,这三棵树是按照一定的规则逐级构建的:

首先,Flutter会根据Widget树中的Widget对象构建对应的Element对象,并把它们按照从上到下、从左到右的顺序连接起来,形成Element树。

接下来,Flutter会根据Element树中的每个Element对象,依次创建对应的RenderObject对象,并把它们按照从上到下、从左到右的顺序连接起来,形成RenderObject树。

最后,Flutter会把RenderObject树提交给底层的图形引擎进行绘制。

在这个过程中,Widget树和Element树是一一对应的,而RenderObject树则是和Element树一一对应的,这样就保证了页面上的每个Widget都能正确地被渲染出来。

相关文章

网友评论

      本文标题:2023-03-28 flutter widget 更新7-先有

      本文链接:https://www.haomeiwen.com/subject/axszrdtx.html