跳转至

底层渲染原理

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(看 runtimeType和 Key)。
  3. 决策:匹配则复用 Element 和 RenderObject(仅更新配置);不匹配则销毁重建。
  4. 渲染:RenderObject 执行 layout和 paint,提交给 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 实现高性能渲染。掌握 Key、const、RepaintBoundary和局部刷新,是进阶开发的必备技能。

RenderObject

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

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

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

只有继承RenderObject才会创建RenderObject。如布局Row、Column继承Flex,Flex继承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对象,RenderObjectElement的mount方法里面就会调用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层级中(例如,不在MaterialApp或Theme的子树中),Theme.of(context)将无法正确获取当前主题。