StatefulWidget
和 StatelessWidget
是 Flutter 中用于构建用户界面的两种基本组件。它们之间的主要区别在于是否拥有可变状态,本质上是对是否需要持久保存状态并响应状态变化的建模抽象;
下文将结合运行周期,适用场景,关键结构进行综合对比分析;
一、运行周期
StatefulWidget 构建
sequenceDiagram
participant FlutterFramework
participant StatefulWidget
participant StatefulElement
participant State
FlutterFramework->>StatefulWidget: 构造函数()
FlutterFramework->>StatefulWidget: createElement()
StatefulWidget->>StatefulElement: 构造 StatefulElement
StatefulElement->>StatefulWidget: createState()
StatefulElement->>State: attach(), set _widget
FlutterFramework->>State: initState()
FlutterFramework->>State: didChangeDependencies()
FlutterFramework->>State: build()
State-->>FlutterFramework: 返回 Widget 树
Note over State: 调用 setState() 后流程
FlutterFramework->>State: setState()
State->>StatefulElement: markNeedsBuild()
FlutterFramework->>State: build()(重新构建)
-
创建 Widget & Element:
- 类似 Stateless,先构造
StatefulWidget
和StatefulElement
;
- 类似 Stateless,先构造
-
状态初始化:
- 调用
createState()
创建并绑定一个State
对象; State
对象接管生命周期,包含状态管理、构建逻辑;
- 调用
-
生命周期初始化:
initState()
在挂载前只调用一次,常用于初始化资源;didChangeDependencies()
在依赖InheritedWidget
时首次或变更时调用;
-
构建 UI:
- 调用
State.build()
,将结果 Widget 树返回给框架;
- 调用
-
状态更新触发构建:
- 调用
setState()
触发markNeedsBuild()
; - 框架在下一帧调用
State.build()
重建子 Widget 树(Element 和 RenderObject 可能重用);
- 调用
StatelessWidget 构建
sequenceDiagram
participant FlutterFramework
participant StatelessWidget
participant StatelessElement
FlutterFramework->>StatelessWidget: 构造函数()
FlutterFramework->>StatelessWidget: createElement()
StatelessWidget->>StatelessElement: 构造 StatelessElement
FlutterFramework->>StatelessElement: mount()
StatelessElement->>StatelessWidget: build()
StatelessElement-->>FlutterFramework: 返回 Widget 树
Note over StatelessElement: 外部状态更新时调用 build()
-
创建实例:
- 框架先调用
StatelessWidget
的构造函数生成 Widget 实例(是配置数据,不保存状态);
- 框架先调用
-
Element 创建:
- 调用
createElement()
方法创建一个StatelessElement
,它代表 Widget 在 Element 树中的位置和生命周期管理;
- 调用
-
挂载阶段(
mount()
):StatelessElement
与其 Widget 配对,并将build()
构建出来的子 Widget 树嵌入父节点;
-
构建 UI:
- 由
StatelessWidget.build()
方法生成 Widget 树,传递回框架以便进一步构建 Element 与 RenderObject。
- 由
-
无状态特点:
- 如果外部触发 UI 更新,会销毁原 Widget 并重新构建,无法保留状态。
二、适用场景
StatefulWidget
适合场景
- 可变状态:
StatefulWidget
可以包含可变的状态(State
),在状态发生变化时重建部分或全部 UI; - 生命周期:
StatefulWidget
有一个关联的State
对象,该对象的生命周期负责管理状态的变化。State
对象在createState
方法中创建,可以在整个组件的生命周期内保持状态; - 场景: 当部分 UI 需要根据用户交互、网络请求或其他异步操作来动态更新时,使用
StatefulWidget
。
示例:
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $_counter'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
- 表单输入、倒计时、动画、交互反馈等内部状态变化。
- 需要控制生命周期的场景(如网络请求绑定释放)。
StatelessWidget
适合场景
- 不可变状态:
StatelessWidget
没有可变的状态,一旦构建完成,其内容就不再改变。 - 生命周期: 由于没有状态变化,
StatelessWidget
不需要关联的State
对象,因此没有生命周期方法。它在构建后就不再改变。 - 场景: 当界面内容在构建后不再变化,且不受用户交互、异步操作等影响时,使用
StatelessWidget
。
示例:
class GreetingWidget extends StatelessWidget {
final String name;
GreetingWidget(this.name);
@override
Widget build(BuildContext context) {
return Text('Hello, $name!');
}
}
- 页面标题栏、静态卡片组件等无状态结构。
- 响应来自外部(如 Provider、Bloc)的状态更新。
小结
场景 | 推荐使用 | 原因 |
---|---|---|
视图稳定,外部驱动 | StatelessWidget | 性能高,代码简洁 |
内部控制状态变更 | StatefulWidget | 可维护生命周期,适用于动画/表单等复杂交互 |
响应框架状态管理 | StatelessWidget | 与 Provider、Riverpod、Bloc 等兼容性强 |
三、结构对比
StatefulWidget 结构
面向生命周期、事件驱动的状态响应机制;
构建 StatefulWidget:
┌─────────────┐
│ Widget.createState() ─────┐
└──────┬────────────────────┘
▼
StatefulElement 持有 State 对象
▼
State.initState()
▼
State.build()
▼
setState → markNeedsBuild → build()
抽象结构
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key? key }) : super(key: key);
@override
StatefulElement createElement() => StatefulElement(this);
@protected
State createState(); // 需子类实现
}
setState 观察者
sequenceDiagram
participant 用户操作
participant State
participant StatefulElement
participant FlutterFramework
用户操作->>State: setState(() { ... })
State->>State: 更新内部变量
State->>StatefulElement: markNeedsBuild()
FlutterFramework->>State: build()
State-->>FlutterFramework: 返回新 Widget 树
abstract class State<T extends StatefulWidget> {
T get widget => _widget;
BuildContext get context => _element;
void initState() {}
void didUpdateWidget(T oldWidget) {}
void setState(VoidCallback fn) { ... } // 状态更新入口
void dispose() {}
}
StatelessWidget 结构
更倾向于“函数式编程思维”:构建就是传参 + 返回视图;
构建 StatelessWidget:
┌─────────────┐
│ Widget.build│
└──────┬──────┘
▼
StatelessElement 负责构建,不保存状态,重建时直接替换。
抽象结构
abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key? key }) : super(key: key);
@override
StatelessElement createElement() => StatelessElement(this);
Widget build(BuildContext context); // 需子类实现
}
小结
- StatefulWidget 适用于存在内部状态的组件,其状态保存在
State
对象中,并通过setState()
驱动重建。 - StatelessWidget 用于构建不可变、纯函数式 UI,不保存状态,
build()
是其核心方法。
两者的 createElement()
方法返回不同的 Element 类型,决定其生命周期和状态管理能力。
总结
特性 | StatelessWidget | StatefulWidget |
---|---|---|
状态是否可变 | ❌ 不可变 | ✅ 可变(可维护局部状态) |
状态保存位置 | Widget 自身 | 通过 State 对象单独维护 |
生命周期 | 构建即结束,无后续生命周期管理 | 有完整生命周期(initState、dispose 等) |
性能开销 | 小,适合轻量 UI | 稍大,需要额外维护 State |
使用场景 | UI 不变、响应外部状态 | UI 受内部状态控制(如表单、动画等) |
从设计模式角度看,StatefulWidget 使用模板方法模式封装生命周期,StatelessWidget 强调函数式声明式编程。选择应根据是否需要内部状态变化、性能开销考虑以及状态是否可通过外部状态管理解决来决定。
在实际开发中,常常会同时使用这两者,将界面拆分为有状态和无状态的组件,以达到更好的组织和性能优化。