深入理解Flutter 源码刷新机制--------一个例子讲清渲染流程

结合前面几篇文章,今天我们例举一个简单的demo说明Flutter刷新机制

demo说明:

创建一个StatefulWidget 里面放置一个定时器,每间隔10s刷新一次,然后将容器背景改成随机颜色

import 'dart:async';
import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(DemoWidget());
}

class DemoWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return DemoState();
  }
}

class DemoState extends State {
  int randomIndex = 0;
  List<Color> colorArray = [Colors.white, Colors.yellowAccent, Colors.green, Colors.lightBlue, Colors.pink];
  @override
  void initState() {
    Timer.periodic(Duration(seconds: 10), (timer) {
      setState(() {
        randomIndex = Random().nextInt(5);
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 50,
      height: 50,
      color: colorArray.elementAt(randomIndex),
    );
  }
}

demo运行起来后,我们打开FlutterDevTools 工具,在浏览器能清晰看出组件树结构

可以看出我们的RenderObject Tree根节点时RenderView ,最后一个页节点是 RenderColoredBox 

当定时器触发后调用setState,state重新build,color会变色,那这个是如何做到的呢

  @protected
  void setState(VoidCallback fn) {
    ...
    _element!.markNeedsBuild();
  }
  void markNeedsBuild() {
    ...
    if (dirty)
      return;
    _dirty = true;
    owner!.scheduleBuildFor(this);
  }
  void scheduleBuildFor(Element element) {
    ...
    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
      _scheduledFlushDirtyElements = true;
      onBuildScheduled!();
    }
    _dirtyElements.add(element);
    element._inDirtyList = true;
   ...
  }
  void _handleBuildScheduled() {
    ...
    ensureVisualUpdate();
  }

以上4个主要方法主要功能就是将当前element标记为dirty,放入到_dirtyElements列表备用。而后给底层引擎发送一个Vsync命令

当下一个Vsync信号驱动到Flutter ,进入_handleBeginFrame 而后进入drawFrame方法

  @override
  void drawFrame() {
    ...
    try {
      if (renderViewElement != null)
        buildOwner!.buildScope(renderViewElement!);
      super.drawFrame();
      buildOwner!.finalizeTree();
    } finally {
      assert(() {
        debugBuildingDirtyElements = false;
        return true;
      }());
    }
    ...
  }

buildOwner !.buildScope 这个方法就是build入口

  void buildScope(Element context, [ VoidCallback? callback ]) {
      ...
        try {
          _dirtyElements[index].rebuild();
        } catch (e, stack) {
          
        }
     ...
  }

可以看到buildScope会从_dirtyElements列表轮询去调用rebuild

  void rebuild() {
    ...
    performRebuild();
    ...
  }
  void performRebuild() {
    ...
    Widget? built;
    built = build();
    try {
      _child = updateChild(_child, built, slot);
      assert(_child != null);
    } catch (e, stack) {
      ...
    }
    ...
  }

performRebuild 会调用build会重新创建widget,然后调用updateChild方法

函数updateChild()比较重要,用来更新一个孩子节点。更新有四种情况:

  • Widget为空,老Widget也为空。则啥也不做。
  • Widget为空,老Widget不为空。这个Element被移除。
  • Widget不为空,老Widget为空。则调用inflateWidget()以这个Wiget为配置实例化一个Element
  • Widget不为空,老Widget不为空。调用update()函数更新子Elementupdate()函数由子类实现。

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

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

  /// The underlying [RenderObject] for this element.
  ///
  /// If this element has been [unmount]ed, this getter will throw.
  @override
  RenderObject get renderObject {
    assert(_renderObject != null, '$runtimeType unmounted');
    return _renderObject!;
  }
  RenderObject? _renderObject;

  bool _debugDoingBuild = false;
  @override
  bool get debugDoingBuild => _debugDoingBuild;

  RenderObjectElement? _ancestorRenderObjectElement;

  ...

  @override
  void update(covariant RenderObjectWidget newWidget) {
    super.update(newWidget);
    ...
    widget.updateRenderObject(this, renderObject);
    ...
    _dirty = false;
  }


 

}

RenderObjectElement 复写了update方法,最后在update方法中看到有widget.updateRenderObject(this, renderObject);这句就是更新RenderObject对象,相对于我们demo RenderColoredBox 就是我们这个变化的RenderObject,ColoredBox就是该RenderObject对应的Widget ,最后会调用到ColordBox的updateRenderObject方法去设置颜色

class ColoredBox extends SingleChildRenderObjectWidget {
  /// Creates a widget that paints its area with the specified [Color].
  ///
  /// The [color] parameter must not be null.
  const ColoredBox({ required this.color, Widget? child, Key? key })
      : assert(color != null),
        super(key: key, child: child);

  /// The color to paint the background area with.
  final Color color;

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _RenderColoredBox(color: color);
  }

  @override
  void updateRenderObject(BuildContext context, RenderObject renderObject) {
    (renderObject as _RenderColoredBox).color = color;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Color>('color', color));
  }
}
  set color(Color value) {
    assert(value != null);
    if (value == _color) {
      return;
    }
    _color = value;
    markNeedsPaint();
  }

看到设置颜色后,重新调用了markNeedsPaint方法,标记当前element需要被重新paint,在后续绘制流过程中,当前element就会重绘

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值