一句话总结:
Flutter UI 的本质不是 Widget,而是 Widget → Element → RenderObject 三层协作。
Widget 只是描述,RenderObject 才是真正画出界面的对象,而 Element 是两者之间的管理者。

本文从底层原理讲到自定义控件,帮助你构建完整、准确、可复用的 Flutter UI 心智模型。

1. 为什么要理解 Widget / Element / RenderObject?

大部分人知道 Flutter 是“声明式 UI”,但长期只写 Widget 很容易产生误解:

  • 为什么 Widget 每帧都能重建但不卡?
  • 为什么 setState 后界面能局部刷新?
  • 为什么自定义控件有时要写 RenderObject?

理解三层结构后,这些问题全部迎刃而解。

2. Flutter UI 的三层结构(核心结论)

Widget(描述)  
        ↓ build()
Element(生命周期管理)  
        ↓ createRenderObject()
RenderObject(布局 + 绘制)

三者分工非常明确:

层级 作用 类比(Android)
Widget UI 的声明,immutable XML、View 的属性
Element 真实 UI 树、生命周期、复用渲染对象 ViewHolder / 中间管理层
RenderObject 负责布局 & 绘制,是最底层核心 View / ViewGroup(onMeasure/onLayout/onDraw)

3. Widget:声明 UI(轻量、不可变)

这是我们平常写的部分,例如:

Text("Hello")
Container(...)
Row(...)

Widget 特点:

  • 不可变(immutable)

  • build() 非常轻量,可以随意重建

  • 不包含尺寸信息、不绘制、不管理状态

  • 只是描述 UI(说明书)

Flutter 性能高的核心之一就是:

Widget 可以频繁重建,因为它们非常轻量。

4. Element:真正的 UI 树(你看不到,但非常重要)

Element 是 Widget 的“实例”,在运行时持久存在。

Element 的三个关键职责:

✔ 1. 构建并维护 Element Tree(真实 UI 树)

Widget Tree 会不断重建,但 Element Tree 才是真正在内存中维持的 UI 树。

✔ 2. 保存 StatefulWidget 的 State

State 挂载在 StatefulElement 上,而不是 Widget 本身。

✔ 3. 复用 RenderObject(性能关键)

当 Widget 更新时:

  • Widget 新实例 ≠ 重新创建渲染对象
  • Element 会复用原有 RenderObject,只更新属性

这就是为什么:

setState 不会让整个树重建,只会局部重建。

5. RenderObject:真正画 UI 的层(布局 + 绘制)

RenderObject 是最核心的底层渲染单位。

它负责两件事:

✔ Layout:计算大小 & 子节点位置

对应 Android 的:

  • onMeasure
  • onLayout

例如:

  • RenderFlex(Row/Column 的布局)
  • RenderStack(堆叠布局)
  • RenderParagraph(Text 布局)

✔ Paint:绘制

对应 Android 的:

  • onDraw(Canvas)

例如:

  • 绘制文本(RenderParagraph)
  • 绘制图片(RenderImage)
  • 绘制背景(RenderDecoratedBox)

一句话:

你在屏幕上看到的像素,都是 RenderObject 画出来的。

6. 开发者平常为什么接触不到 Element 和 RenderObject?

因为 Flutter 做到了最佳封装:

  • 你写 Widget(声明 UI)
  • Element 自动帮你管理生命周期/状态/复用渲染对象
  • RenderObject 自动进行布局和绘制

绝大多数场景,你完全不需要关心 Element 和 RenderObject。

写 Widget 就够了。

7. 自定义 Widget 分四层(非常重要)

第一层:组合型自定义 Widget(最常用,几乎全靠它)

你写:

class MyCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.white),
      child: Column(
        children: [
          Text("Title"),
          SizedBox(height: 10),
          Text("Content"),
        ],
      ),
    );
  }
}

你只是把:

  • Container

  • Text

  • Column

组合成一个新的“小组件”。

🔸 这是最常见的自定义 Widget
🔸 根本不会接触 Element、RenderObject
🔸 Flutter 会自动帮你创建 Element/RenderObject

你只需要写 build()。

第二层:带 State 的自定义 Widget(StatefulWidget)

比如计数器:

class Counter extends StatefulWidget {
  @override
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return Text("$count");
  }
}

你还是在写 Widget,不接触 Element/RenderObject。

虽然底层是:

  • StatefulElement
  • State 存在 Element 中

但你不需要操作它们。

第三层:CustomPaint(轻绘制),但不自定义布局

如果你想画图:

CustomPaint(
  painter: MyPainter(),
);

写一个 painter:

class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawCircle(...);
  }
}

👉 这里开始接触到绘制了,但不是 RenderObject。
👉 painter = “轻量绘制层”,它帮你画图,但不能改变布局。

你仍然没进入 RenderObject 的世界。

第四层(高级):自定义 RenderObject(真正底层)

只有下面情况才需要:

  • 自己实现布局算法(比如瀑布流)
  • 自己实现性能更高的控件
  • 需要读/写 RenderObject 属性
  • 需要完全控制 Layout + Paint

你才会写:

class MyBox extends LeafRenderObjectWidget {
  @override
  RenderObject createRenderObject(BuildContext context) {
    return MyRenderBox();
  }
}

class MyRenderBox extends RenderBox {
  @override
  void performLayout() {
    size = constraints.constrain(Size(100, 50));
  }

  @override
  void paint(PaintingContext ctx, Offset offset) {
    ctx.canvas.drawRect(offset & size, Paint()..color = Colors.red);
  }
}

👉 这个才叫真正“自定义 RenderObject Widget”
👉 一般业务场景 99% 用不到
👉 这种属于 SDK 级别、轮子级别

对应 Android 的:

自定义 View(onMeasure / onLayout / onDraw)

例如:

  • 自定义 Flow Layout
  • 自定义瀑布流
  • 可拖拽控件
  • 高性能图形控件
  • 完全自定义 UI 组件

8. RenderObject vs Android 自定义 View(非常关键的对应)

Android Flutter 相同点
View RenderObject 渲染核心
onMeasure performLayout 测量
onLayout performLayout 内为子布局 布局
onDraw(Canvas) paint(Canvas) 绘制
invalidate() markNeedsLayout/paint 标脏重绘
ViewGroup MultiChildRenderObject 多子布局

可以说:

Flutter 自定义 RenderObject = Android 自定义 View(本质一致)

9. 整体体系图(建议你用于 PPT)

               Widget Tree(声明)
                      ↓
           Element Tree(生命周期、复用)
                      ↓
           Render Tree(布局 + 绘制)
                      ↓
                显示到屏幕

再配一句:

你写的是 Widget,真正画的是 RenderObject。
中间由 Element 负责连接和复用。

扩展

近年来,前端框架(React、Vue)与跨平台 UI 框架(Flutter)虽然看似来自不同世界,但它们在核心思想上却惊人地一致:
都采用了声明式 UI、虚拟节点、框架调度、局部更新、自动渲染管线等理念。

很多学习 Flutter 的开发者在深入理解 Widget、Element、RenderObject 三层后都会产生一个直觉:

这不就是 Flutter 版本的 React/Vue 吗?
Flutter 甚至把 Virtual DOM 替换成自己的 Widget/Element/RenderObject!

结论:

Flutter 的三层体系和 React / Vue 的思想高度一致

下一篇:
 

Flutter 与 React/Vue 为什么思想一致?——声明式 UI 体系的深度对比(超清晰版)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐