在 Flutter 中,"一切皆 Widget"。理解 Widget 的本质、树结构与生命周期,是掌握 Flutter UI 开发的基石。
一、StatelessWidget 与 StatefulWidget
1.1 StatelessWidget(无状态组件)
StatelessWidget 是不可变的,一旦创建其属性无法改变,适合纯展示型 UI:
dart
class UserCard extends StatelessWidget {
final String name;
final String avatar;
final bool isOnline;
const UserCard({
super.key,
required this.name,
required this.avatar,
this.isOnline = false,
});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: CircleAvatar(backgroundImage: NetworkImage(avatar)),
title: Text(name),
trailing: Icon(
Icons.circle,
color: isOnline ? Colors.green : Colors.grey,
size: 12,
),
),
);
}
}
使用场景:
- 展示静态内容(文字、图片、图标)
- 仅依赖外部传入的参数渲染
- 无需维护内部状态
1.2 StatefulWidget(有状态组件)
StatefulWidget 与 State 对象分离,State 可变且持久化:
dart
class CounterWidget extends StatefulWidget {
final int initialCount;
const CounterWidget({super.key, this.initialCount = 0});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
late int _count;
@override
void initState() {
super.initState();
_count = widget.initialCount; // 通过 widget 访问 StatefulWidget 属性
}
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('+1'),
),
],
);
}
@override
void dispose() {
// 释放资源(控制器、流订阅等)
super.dispose();
}
}
1.3 对比总结
| 对比项 | StatelessWidget | StatefulWidget |
|---|---|---|
| 状态 | 无内部状态 | 有可变内部状态 |
| 可变性 | 不可变 | State 可变 |
| 性能 | 更高效 | 状态变化时重建 |
| 适用场景 | 纯展示 | 交互、动画、异步 |
| 生命周期 | build() | initState/build/dispose |
二、Widget 树与 Element 树
2.1 三棵树
Flutter 内部维护三棵并行的树:
Widget Tree (不可变描述)
↓ 创建/挂载
Element Tree (可变实例,持有 State)
↓ 驱动
RenderObject Tree (布局与绘制)
| 树 | 职责 | 生命周期 |
|---|---|---|
| Widget Tree | UI 声明(不可变的配置描述) | 频繁重建 |
| Element Tree | 持久化的实例,持有 State | 尽量复用 |
| RenderObject Tree | 实际布局与绘制 | 更新最小化 |
2.2 build() 机制
dart
@override
Widget build(BuildContext context) {
// build() 应该是纯函数,不产生副作用
// 可以被多次调用,应保持幂等性
return Container(
color: Theme.of(context).primaryColor, // 通过 context 读取主题
child: Text(_count.toString()),
);
}
重要原则:
build()应为纯函数(无副作用)- 不在
build()中做耗时操作(网络请求、数据库查询) - Flutter 会尽可能复用 Element,只重建必要部分
2.3 什么时候触发 build()?
| 触发条件 | 说明 |
|---|---|
setState() 调用 |
当前 State 及其子树标记为 dirty |
| 父 Widget 重建 | 父 build() 返回新 Widget 时 |
InheritedWidget 变化 |
依赖的 InheritedWidget 数据更新 |
| Hot Reload | 开发时强制重建 |
三、Key 的作用
Key 帮助 Flutter 识别 Widget 在树中的身份,实现正确的 Element 复用与状态保持。
3.1 为什么需要 Key?
dart
// 问题示例:交换两个有状态计数器
// 没有 Key 时,Flutter 只看 Widget 类型,可能复用错误的 State
Row(children: [
CounterWidget(), // ❌ 交换位置后,State 不随 Widget 移动
CounterWidget(),
])
// 使用 Key 后,Flutter 能正确识别并移动对应 State
Row(children: [
CounterWidget(key: ValueKey('counter_a')), // ✅
CounterWidget(key: ValueKey('counter_b')),
])
3.2 LocalKey 类型
dart
// ValueKey:用于值唯一的场景(ID、字符串等)
ListView.builder(
itemBuilder: (context, index) => ListItem(
key: ValueKey(items[index].id), // 使用数据 ID 作为 Key
item: items[index],
),
)
// ObjectKey:用于对象引用唯一的场景
ListItem(key: ObjectKey(userObject))
// UniqueKey:每次构建都生成新 Key(强制重建)
Container(key: UniqueKey()) // 每次 build 都是全新实例
3.3 GlobalKey
GlobalKey 可以在 Widget 树任意位置访问对应的 State 或 RenderObject:
dart
final formKey = GlobalKey<FormState>();
final scaffoldKey = GlobalKey<ScaffoldState>();
// 在 build 中
Form(
key: formKey,
child: Column(children: [
TextFormField(...),
ElevatedButton(
onPressed: () {
// 通过 GlobalKey 访问 FormState
if (formKey.currentState!.validate()) {
formKey.currentState!.save();
}
},
child: const Text('提交'),
),
]),
)
⚠️ 注意: GlobalKey 开销较大,不要滥用。优先使用 LocalKey。
四、Widget 的生命周期
4.1 StatefulWidget 生命周期
创建 → initState() → didChangeDependencies() → build()
↑
更新 → setState() / didUpdateWidget() → build() ───┘
销毁 → deactivate() → dispose()
dart
class LifecycleDemo extends StatefulWidget {
final String param;
const LifecycleDemo({super.key, required this.param});
@override
State<LifecycleDemo> createState() => _LifecycleDemoState();
}
class _LifecycleDemoState extends State<LifecycleDemo> {
late StreamSubscription _sub;
@override
void initState() {
super.initState();
// ✅ 初始化:只调用一次
// 订阅流、初始化控制器、获取初始数据
_sub = someStream.listen((data) => setState(() {}));
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// ✅ 依赖的 InheritedWidget 变化时调用
// 第一次 build 前也会调用
}
@override
void didUpdateWidget(covariant LifecycleDemo oldWidget) {
super.didUpdateWidget(oldWidget);
// ✅ 父 Widget 重建导致本 Widget 配置更新时调用
if (oldWidget.param != widget.param) {
// 响应参数变化
}
}
@override
Widget build(BuildContext context) {
return Text(widget.param);
}
@override
void deactivate() {
// ✅ 从树中移除时调用(可能重新插入,如 Navigator)
super.deactivate();
}
@override
void dispose() {
// ✅ 永久销毁,释放所有资源
_sub.cancel();
super.dispose();
}
}
4.2 生命周期方法详解
| 方法 | 调用时机 | 常见用途 |
|---|---|---|
initState() |
创建后第一次调用,仅一次 | 初始化数据、订阅流 |
didChangeDependencies() |
依赖变化时 | 读取 InheritedWidget |
build() |
有变化需要重绘时 | 返回 Widget 树 |
didUpdateWidget() |
父传参变化时 | 响应参数更新 |
deactivate() |
从树中暂时移除 | 暂停操作 |
dispose() |
永久销毁 | 取消订阅、关闭控制器 |
五、组合优于继承
Flutter 强调通过组合 Widget 而非继承来构建复杂 UI。
5.1 Widget 组合模式
dart
// ✅ 组合方式:将多个小 Widget 组合成大 Widget
class ProfileHeader extends StatelessWidget {
final User user;
const ProfileHeader({super.key, required this.user});
@override
Widget build(BuildContext context) {
return Column(
children: [
AvatarWidget(url: user.avatarUrl), // 独立头像组件
UserNameWidget(name: user.name), // 独立名称组件
FollowButton(userId: user.id), // 独立关注按钮
],
);
}
}
5.2 自定义 Widget 最佳实践
dart
// ✅ 职责单一:一个 Widget 做一件事
class PrimaryButton extends StatelessWidget {
final String label;
final VoidCallback? onPressed;
final bool isLoading;
const PrimaryButton({
super.key,
required this.label,
this.onPressed,
this.isLoading = false,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(48),
),
child: isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(label),
);
}
}
小结
| 知识点 | 核心要点 |
|---|---|
| StatelessWidget | 不可变,纯展示,性能高 |
| StatefulWidget | State 可变,setState 触发重建 |
| 三棵树 | Widget(描述)→ Element(实例)→ RenderObject(渲染) |
| Key | 帮助 Flutter 正确识别和复用 Element,避免状态错误 |
| GlobalKey | 跨 Widget 访问 State,慎用 |
| 生命周期 | initState → build → didUpdateWidget → dispose |
| 组合优于继承 | 小组件组合成大组件,保持职责单一 |
👉 下一节:2.2 布局系统