跳转至

底层渲染原理

flutter修改UI,核心渲染:增量渲染,哪个widget变了就渲染谁。所以不变的定义常量const。

flutter没有图层,只有一层页面,整个页面是一个树。并不是全部都渲染,只渲染(新建)树中改变的节点widget。想要改变某一个控件的值,直接新创建一个widget对象控件,把原来的替换掉。

下面拆解 Widget、Element、RenderObject 三棵树 的协作机制,把“代码”变成“屏幕像素”的过程彻底透明化。

为什么是“三棵树”

Flutter 采用分层架构是为了极致性能。它通过“轻量级配置”驱动“重量级渲染”,实现了最小化更新

  • Widget:轻量级“设计图”,负责描述 UI,频繁重建成本极低。
  • Element:核心“调度员”,负责管理生命周期和状态,决定是否复用。
  • RenderObject:重量级“施工队”,真正负责布局和绘制,尽量复用。

三棵树的分工与协作

角色 职责 关键特性
Widget Tree 设计图 (Blueprint) 描述 UI 长什么样(配置) 不可变(Immutable),重建代价极低,善用 const
Element Tree 施工队长 (Foreman) 连接配置与实际渲染,持有 State Diff 算法核心,决定复用还是重建
RenderObject Tree 施工队 (Worker) 执行布局(Layout)和绘制(Paint) 开销巨大,通过脏标记(Dirty)局部更新

协作流程(setState 背后)

  1. 触发setState()标记 Element 为 dirty
  2. Diff:下一帧,Element 对比新旧 Widget(看 runtimeTypeKey)。
  3. 决策:匹配则复用 Element 和 RenderObject(仅更新配置);不匹配则销毁重建。
  4. 渲染:RenderObject 执行 layoutpaint,提交给 GPU。

关键机制与性能优化

1. Element 的 Diff 与复用(性能基石)

  • Key 的作用:在动态列表或排序场景中,Key是 Element 复用的唯一标识。没有 Key 会导致状态错乱(如列表项状态“串位”)。
  • 复用条件:新旧 Widget 的 runtimeType相同且 Key匹配。

2. RenderObject 的脏标记机制

  • markNeedsLayout():尺寸/位置变了,触发重新布局+绘制。
  • markNeedsPaint():仅颜色/透明度变了,只重绘,跳过布局(性能更好)。
  • RepaintBoundary:将高频动画组件(如 Lottie)隔离为独立图层,避免其重绘导致整个页面重绘。

3. 状态(State)的真实归属

State 对象由 Element 持有,而非 Widget。Widget 只是不可变的配置描述。这就是为什么 Widget 重建后,State 中的数据依然存在(因为 Element 没换)。


实战避坑与最佳实践

误区 问题 解决方案
滥用 GlobalKey 强制 Element 跨树迁移,破坏 Diff,性能极差 优先使用 ValueKey/ObjectKey
build() 里做重计算 每次 UI 刷新都重复计算,导致卡顿 缓存计算结果,或用 didUpdateWidget控制重算
忽视 const 每次重建都创建新对象,增加 Diff 负担 对静态 Widget 加 const,编译期单例,零开销
setState 范围过大 导致整棵子树重建 使用 ValueListenableBuilder或状态管理库局部刷新

调试工具(DevTools)观察三棵树

打开 Flutter DevTools → Widget Inspector,你可以实时看到:

  1. Widget Tree 面板:查看当前组件树结构
  2. 选中某个 Widget:右侧显示对应的 Element 信息(类型、Key、BuildContext)
  3. Performance Overlay:开启帧渲染监控,定位 RenderObject 的布局/绘制瓶颈
  4. Repaint Rainbow:开启后每次重绘区域变色,直观看出哪些区域在不必要地重绘
// main.dart 中开启 Repaint Rainbow(仅调试用)
import 'package:flutter/rendering.dart';
void main() {
  debugRepaintRainbowEnabled = true; // 开启后重绘区域会闪烁彩色边框
  runApp(MyApp());
}

💡 底层原理延伸

  • BuildContext 本质:就是 Element对象本身,通过它可以在树中向上查找祖先(如 Theme.of(context))。
  • InheritedWidget:状态管理(Provider/Riverpod)的底层基础。利用 Element 树建立依赖关系,数据变更时精准通知依赖项重建。

总结:理解三棵树,你就理解了 Flutter 如何用声明式 UI 实现高性能渲染。掌握 KeyconstRepaintBoundary和局部刷新,是进阶开发的必备技能。

RenderObject

flutter渲染的是RenderObject树。与渲染有关的是Render树。

不是所有的Widget都会生成Render树,所以并不是所有的Widget都会显示到屏幕上。

Container继承StatelessWidgetStatelessWidget继承Widget,不会创建RenderObject对象。

只有继承RenderObject才会创建RenderObject。如布局RowColumn继承FlexFlex继承RenderObjectWidget。Flex重写了父类的createRenderObject

abstract class RenderObjectWidget extends Widget {
  const RenderObjectWidget({ Key? key }) : super(key: key);

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

  @protected
  @factory
  /// 抽象方法,子类去实现
  RenderObject createRenderObject(BuildContext context);

  ///省略代码
}

所有继承Widget的都会创建Element对象。Widget和Element一一对应。没有继承Render的也有createElement

Element会调用mount方法。如果部件是继承RenderObjectWidget对象,RenderObjectElementmount方法里面就会调用widget.createRenderObject方法。

Element把Widget和Render关联在一起。

Element相当于中间层。

Widget

StatelessWidget

abstract class StatelessWidget extends Widget {
  const StatelessWidget({ Key? key }) : super(key: key);

  @override
  //创建element对象
  StatelessElement createElement() => StatelessElement(this);//this就是widget

  @protected
  Widget build(BuildContext context);
}

调用StatelessWidget的build方法

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);//调用StatelessWidget的build方法

  @override
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    _dirty = true;
    rebuild();
  }
}

ComponentElement里面会调用mount方法,然后一层一层的调用,最终拿出widget对象调用build方法。

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

  Element? _child;

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

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

  void _firstBuild() {
    rebuild();
  }

  @override
  @pragma('vm:notify-debugger-on-exception')
  void performRebuild() {
    if (!kReleaseMode && debugProfileBuildsEnabled)
      Timeline.startSync('${widget.runtimeType}',  arguments: timelineArgumentsIndicatingLandmarkEvent);

  /// 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();

    /// 省略代码
}

Element

abstract class Element extends DiagnosticableTree implements BuildContext {
  /// Creates an element that uses the given widget as its configuration.
  ///
  /// Typically called by an override of [Widget.createElement].
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;//_widget变量赋值

    /// 省略代码
}

创建widget会调用createElement方法创建element,element创建完之后会调用外面widget的build,并把element作为context传出去。

总结:

  1. statelessWidget会创建Element
  2. Element创建会调用mount方法
  3. mount方法会调用widget的build方法进行渲染,并且将Element自己传出去(BuildContext context)

StatefulWidget

widget和state共用一个element

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key? key }) : super(key: key);

  @override
  StatefulElement createElement() => StatefulElement(this);//this就是widget

  @protected
  @factory
  State createState(); // ignore: no_logic_in_create_state, this is the original sin
}
class StatefulElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
  /// 创建element对象,创建的时候多了一个创建State并把state保存
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
        /// assert
    state._element = this;//widget和state公用一个element
    state._widget = widget;//把widget保存到state的_widget
    assert(state._debugLifecycleState == _StateLifecycle.created);
  }

  @override
  Widget build() => state.build(this);//state的build this是element

    /// 省略代码
}

总结:

在Flutter渲染的流程中,有三棵树,Flutter引擎渲染是针对Render树中的对象进行渲染。

每一个widget创建出来都会创建一个Element对象

调用createElement方法。Element加入Element树中,都会调用mount方法。

  1. RanderElement继承RenderObjectElement继承Element

主要创建RenderObject对象,通过mount方法创建RenderObject对象。

  1. StatefulElement继承ComponentElement

  2. 调用createState方法,创建state

  3. 将Widget赋值给State对象
  4. 调用state的build方法,并且将自己(Element)传出去

  5. StatelessElement继承ComponentElement

主要调用build方法,并且将自己(Element)传出去

深色模式

Theme.of(context)依赖于context的位置。如果context不在正确的BuildContext层级中(例如,不在MaterialAppTheme的子树中),Theme.of(context)将无法正确获取当前主题。