1. 开场:为什么要讲这个?
80% 的 Flutter 初学者都搞不清楚 “什么时候用 Stateless?什么时候用 Stateful?”
写着写着就全变成 StatefulWidget。
页面卡顿其实不是 Flutter 性能问题,而是 写法影响 rebuild 范围。
Flutter 最大的思想:UI = 函数(state)。
2. StatelessWidget — 无状态组件
✅ 定义
UI 仅由 外部传入的数据 决定,自身不保存状态。
特点
| 特性 | 说明 |
|---|---|
| 无内部状态 | 没有 setState |
可 const | 直接复用,减少 rebuild |
| 更轻量 | 构建快,生命周期简单 |
生命周期
build()
什么时候用?
页面展示 UI,不需要在本组件内改变状态
数据变化靠 父组件传参 或 状态管理(Riverpod / Provider / Bloc)
示例
class UserAvatar extends StatelessWidget {final String url;const UserAvatar(this.url);@overrideWidget build(BuildContext context) {return Image.network(url);}
}
3. StatefulWidget — 有状态组件
✅ 定义
有一个
State对象保存状态,需要更新 UI 时调用 setState()。
特点
| 特性 | 说明 |
|---|---|
| 自己保存状态 | 用 setState 更新 UI |
| 有生命周期 | 有 init / dispose |
| 用于交互逻辑 | 表单、滚动、动画、输入框控制器等 |
生命周期(简)
createState()
initState()
didChangeDependencies()
build() <-- 可以调用很多次
dispose()
示例:计数器
class Counter extends StatefulWidget {@overrideState createState() => _CounterState();
}
class _CounterState extends State {int count = 0;@overrideWidget build(BuildContext context) {return ElevatedButton(onPressed: () => setState(() => count++),child: Text("count = $count"),);}
}
4. 关键对比
| 对比点 | StatelessWidget | StatefulWidget |
|---|---|---|
| 是否保存状态 | ❌ 不保存 | ✅ 保存 |
| UI 是否可更新 | ✅ 外部变化可更新 | ✅ 内部随时更新 |
| 生命周期 | 简单 | 拥有完整生命周期 |
| 性能与稳定性 | ✅ 更好 | ⚠️ 控制不好会 rebuild 太多 |
| 推荐使用场景 | 展示型 UI | 交互/动画/控制器/表单等 |
5. 小总结:怎么选?
不要因为会 setState 就全部用 StatefulWidget。
流程图:
是否需要内部保存状态?└── No → StatelessWidget└── Yes → StatefulWidget
进一步细分:
✅ 局部状态 → Stateful
✅ 跨组件状态 → Riverpod / Bloc
✅ UI 仅依赖外部数据 → Stateless (能 const 就 const)
6. 实战:用 Stateless + Riverpod 代替 Stateful
class UserNameText extends ConsumerWidget {@overrideWidget build(BuildContext context, WidgetRef ref) {final name = ref.watch(userProvider);return Text(name);}
}
数据和 UI 解耦:UI 变成 Stateless,状态交给 Riverpod/Bloc。
7. 常见误区与最佳实践
| 错误 | 说明 |
|---|---|
| ✅ 全部用 Stateful | 太多 setState 会导致不必要的 rebuild |
| ✅ 把 Controller 放 build() 里 | 会不断创建/销毁,导致内存泄漏 |
| ✅ 不写 dispose() | 控制器没释放,会泄漏 |
最佳实践:
@override
void initState() {super.initState();controller = TextEditingController();
}
@override
void dispose() {controller.dispose();super.dispose();
}
8. 结论
能 Stateless 不 Stateful
状态尽量外置(Riverpod / Bloc)
局部交互才用setState