每日一题 Flutter#2 | 如何理解 Widget 的不可变性

最近在着手开发我的 《匠心星问》 ,它定位是一款 题库 应用,将集题目浏览、发布、解答、做题为一体。打算第一步先以 Flutter 为核心,准备题库资源。于是诞生《每日一题》 系列,准备精心设计一些 Flutter 的问题与解答,作为题库的养料。本文的焦点是探讨:

如何理解 Widget 的不可变性

Widget 的不可变性是 Flutter 实现热重载、高性能渲染以及组件复用的基础,是理解 Flutter 架构和开发思维的重要一环。理解 Widget 的不可变性,是打开 Flutter 编程之门的钥匙。

- -

1. 什么是 Widget 的不可变性

在 Flutter 中,Widget 是构建 UI 的基本单元, 在 Flutter 源码 中对 Widget 类注释中,有着这样一句表述 :

一个 Widget 是对 UI 界面某部分 不可变的描述

许多初学者常常对 Widget「不可变」的特性感到困惑。简单来说,不可变性指的是:

一旦 Widget 对象被创建,其属性就不能更改。


比如下面的 Text 组件创建之后,其中的 张风捷特烈 内容以及样式等配置数据,都不能变动:

dart 复制代码
Text text = Text('张风捷特烈');

如果你想让字号变大呈红色 , 此时你 不能修改 上面 text 对象的任何属性,而是要 创建一个新的Text对象 来替代原来的。

dart 复制代码
❌ 为样式赋值修改
text.style = TextStyle(fontSize: 20, color: Colors.red) 

✅ 创建新的 Text 对象
Text newText = Text(
  '张风捷特烈',
  style: TextStyle(fontSize: 20, color: Colors.red),
)

2. Widget 不可变性的保证

如何在代码层面保证使用者不会修改 Widget 的成员呢? 答案很简单: 使用 final 修饰

熟悉 Flutter 的朋友可能都知道,官方所有组件的所有普通成员,都会通过 final 关键字修饰(如下 Text 组件)。着就从根本上扼杀了开发者修改属性的可能:


而如果你尝试在自定义的 Widget 中定义一个非 final 修饰的普通成员时,就会收到一个警告,提醒你 Widget 的所有属性都需要是不可变的。

这两者就可以保证在开发过程中,开发者 不会不能 修改已存在的 Widget 对象。


3. 不可变性的意义是什么

在 Flutter 中,Widget 并不是动态变化的 UI 元素 ,它更像是一张 描述当前 UI 的结构图------就像你在某一时刻截下的一张静态 UI 照片。这张图不会变,它只是告诉框架"这一帧 UI 应该长什么样"。

当状态发生变化时,Flutter 不会去修改已有的 Widget,通过重建Widget树生成新的配置描述,形成下一帧的新"快照"。 这样通过 状态数据布局结构 就能重现当时的界面,也就是可预测 UI 状态。

  • 符合声明式编程范式 :不可变性是声明式 UI 的核心,能根据状态构建出完整的界面,而无需手动操作更新流程。
  • 简化 UI 构建逻辑 : 不可变性让 Widget 成为一种 纯 UI 描述,代码更易于理解和维护。
  • 状态分离更清晰 :所有变化都交由状态数据管理处理,Widget 本身保持纯净,有助于模块化和复用。
  • 提升性能 : 空间可以通过比较新旧 Widget 来判断是否复用底层的渲染对象,避免不必要的重建和开销。
  • 热重载的基础 : 不可变的 Widget 架构,让 Flutter 能够快速替换 UI 树中的节点,从而实现毫秒级的热重载体验。

4. Widget 不可变,界面如何变化呢?

我们都知道,在 Flutter 中 Widget 是界面表现的决定因素,那么 Widget 不可变,是否意味着 界面不可变 呢? 答案显然是否定的。

Widget 是某一帧界面的配置数据,Flutter 的渲染机制是基于 重建(build) 思想运作的:每当状态数据发生变化,Flutter 并不会去修改已有的 Widget,而是重新构建新的 Widget 。来描述在当前状态下,界面应有的样子。Flutter 会将其与上一帧的 Widget 树进行对比,找出差异,并仅更新需要变化的部分。这种 状态驱动 + Widget 重建 的方式,使得 UI 变化既高效又可控,同时充分发挥了声明式编程的优势。


打个比方,在餐厅点餐,菜单(Widget)是不会变的 ,它只是描述了每道菜长什么样、有哪些配料。你点了一道超辣牛肉热拌面,订单提交后。但过一会你改变口味(状态变化),想起来今天不能吃辣。服务员不会去 修改 你原来的订单,而是重新生成一份新的 不辣牛肉热拌面 订单。

同样地,Flutter 并不会修改原来的 Widget,而是根据当前状态重新 "上菜" ------ 构建一个新的 Widget。这种做法避免了直接修改原有内容带来的混乱,也让整个界面的更新过程更加清晰、有序、高效。你看到的"界面变化",其实是状态变化后,框架重新声明了一道 "新菜"。


5. 小结

我在这里给出这道题的简要回答:

Q:如何理解 Widget 的不可变性
A: 在 Flutter 中,Widget 是构建 UI 界面的基本单位,它本质上是不可变(immutable) 的。所谓不可变,指的是一旦 Widget 被创建,其属性就不能再被修改。每一个 Widget 实例只是用来描述某一帧 UI 的外观,它本身 不持有 布局、渲染或状态等信息数据。

理解这一特性对于掌握 Flutter 的声明式 UI 编程至关重要。因为 Widget 不可变,界面的变化并不是修改旧的 Widget,而是根据新的状态,重新构建新的 Widget 。Flutter 会将新旧 Widget 树进行对比(diff),并智能地更新 Element 树和底层渲染结构,只改变必要的部分,从而实现高效渲染。


如果你有其他的看法,或者有什么想要的题目、或者想提供题目和答案,都欢迎在评论区留言。更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。

相关推荐
小悟空7 小时前
[AI 生成] Flink 面试题
大数据·面试·flink
你听得到119 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构
Jackilina_Stone9 小时前
【faiss】用于高效相似性搜索和聚类的C++库 | 源码详解与编译安装
android·linux·c++·编译·faiss
Sherry0079 小时前
CSS Grid 交互式指南(译)(下)
css·面试
一只毛驴9 小时前
浏览器中的事件冒泡,事件捕获,事件委托
前端·面试
一只叫煤球的猫9 小时前
你真的处理好 null 了吗?——11种常见但容易被忽视的空值处理方式
java·后端·面试
棒棒AIT10 小时前
mac 苹果电脑 Intel 芯片(Mac X86) 安卓虚拟机 Android模拟器 的救命稻草(下载安装指南)
android·游戏·macos·安卓·mac
KarrySmile10 小时前
Day04–链表–24. 两两交换链表中的节点,19. 删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II
算法·链表·面试·双指针法·虚拟头结点·环形链表
fishwheel10 小时前
Android:Reverse 实战 part 2 番外 IDA python
android·python·安全
一只毛驴11 小时前
谈谈浏览器的DOM事件-从0级到2级
前端·面试