## 一、引言
在 Flutter 开发中,理解 Widget 的生命周期是至关重要的。Widget 生命周期描述了 Widget 从创建到销毁的整个过程,掌握生命周期可以帮助我们:
- **正确初始化资源**:在合适的时机初始化数据、订阅流、启动定时器等
- **优化性能**:避免不必要的重建,合理使用缓存
- **防止内存泄漏**:及时清理资源,取消订阅
- **处理依赖变化**:响应主题、语言、屏幕尺寸等变化
本文将详细介绍 Flutter StatefulWidget 的完整生命周期,帮助你深入理解每个阶段的含义和最佳实践。
## 二、生命周期概览
Flutter Widget 的生命周期可以分为三个阶段:
```mermaid
flowchart TD
A[创建阶段] --> B[运行阶段]
B --> C[销毁阶段]
A1[1. 构造函数] --> A2[2. createState]
A2 --> A3[3. initState]
A3 --> A4[4. didChangeDependencies]
A4 --> B
B1[5. build] --> B2{父组件更新?}
B2 -->|是| B3[6. didUpdateWidget]
B3 --> B1
B2 -->|否| B4[setState 调用]
B4 --> B1
B --> C1[7. dispose]
style A fill:#90EE90
style B fill:#87CEEB
style C fill:#FFB6C1
```
### 三个阶段说明
1. **创建阶段**:Widget 被创建并插入到 Widget 树中
2. **运行阶段**:Widget 在树中运行,可能多次重建
3. **销毁阶段**:Widget 从树中移除并销毁
## 三、创建阶段详解
### 1. 构造函数(Constructor)
**调用时机**:创建 Widget 实例时立即调用
**用途**:
- 接收父组件传递的参数
- 初始化 Widget 的不可变属性
- 设置默认值
**特点**:
- 在 `createState()` 之前调用
- 可以接收 `key` 参数用于 Widget 识别
**示例**:
```dart
class MyWidget extends StatefulWidget {
final String title;
final int count;
// 构造函数
const MyWidget({
Key? key,
required this.title,
this.count = 0,
}) : super(key: key);
@override
State<MyWidget> createState() => _MyWidgetState();
}
```
**注意事项**:
- 构造函数应该是 `const` 的(如果可能)
- 使用 `required` 标记必需参数
- 避免在构造函数中执行耗时操作
### 2. createState()
**调用时机**:构造函数之后立即调用
**用途**:
- 创建对应的 State 对象
- 每个 StatefulWidget 必须实现此方法
**特点**:
- 只调用一次
- 返回 State 实例
- 在 `initState()` 之前调用
**示例**:
```dart
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
// State 类的实现
}
```
**注意事项**:
- 必须返回一个 State 实例
- 通常返回私有 State 类(以下划线开头)
### 3. initState()
**调用时机**:State 对象创建后,在第一次 `build()` 之前调用
**用途**:
- 初始化状态变量
- 订阅数据流(Stream、ChangeNotifier 等)
- 启动定时器
- 初始化控制器(AnimationController、TextEditingController 等)
- 执行一次性的初始化操作
**特点**:
- **只调用一次**(在整个生命周期中)
- 在 `build()` 之前调用
- 可以调用 `setState()`,但通常不推荐(因为此时还未构建 UI)
**示例**:
```dart
class _MyWidgetState extends State<MyWidget> {
late TextEditingController _controller;
StreamSubscription? _subscription;
Timer? _timer;
@override
void initState() {
super.initState();
// 初始化控制器
_controller = TextEditingController();
// 订阅数据流
_subscription = someStream.listen((data) {
setState(() {
// 更新状态
});
});
// 启动定时器
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
// 定时任务
});
// 初始化状态变量
_loadInitialData();
}
void _loadInitialData() {
// 加载初始数据
}
}
```
**最佳实践**:
- 始终调用 `super.initState()`
- 初始化所有需要在 `build()` 中使用的变量
- 避免执行耗时操作(使用异步方法)
- 不要在这里访问 `BuildContext` 的依赖(如 Theme、MediaQuery),应使用 `didChangeDependencies()`
### 4. didChangeDependencies()
**调用时机**:
- 在 `initState()` 之后立即调用一次
- 当 Widget 依赖的 `InheritedWidget` 发生变化时再次调用
**依赖类型**:
- 🎨 **Theme**:主题数据(颜色、文本样式等)
- 📱 **MediaQuery**:屏幕尺寸、设备方向等
- 🌍 **Localizations**:国际化数据(语言、地区等)
- 📦 **Provider**:状态管理数据(如果使用 Provider)
**用途**:
- 访问 `BuildContext` 的依赖数据
- 响应依赖变化,更新相关状态
- 注册依赖监听(如果需要)
**特点**:
- 在 `initState()` 之后调用
- 可能被多次调用(当依赖变化时)
- 可以安全地访问 `BuildContext` 的依赖
**示例**:
```dart
class _MyWidgetState extends State<MyWidget> {
late ThemeData _theme;
late MediaQueryData _mediaQuery;
String? _locale;
@override
void initState() {
super.initState();
// 这里还不能访问 Theme、MediaQuery 等
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 现在可以安全地访问依赖了
_theme = Theme.of(context);
_mediaQuery = MediaQuery.of(context);
_locale = Localizations.localeOf(context).toString();
// 响应依赖变化
if (_mediaQuery.orientation != Orientation.portrait) {
// 处理横屏逻辑
}
}
}
```
**最佳实践**:
- 始终调用 `super.didChangeDependencies()`
- 检查依赖是否真的变化了,避免不必要的更新
- 如果只需要在初始化时访问依赖,可以在第一次调用时处理
## 四、运行阶段详解
### 5. build()
**调用时机**:
- `didChangeDependencies()` 之后立即调用
- 每次 `setState()` 被调用时
- 父 Widget 重建导致子 Widget 重建时
- `didUpdateWidget()` 之后
**用途**:
- 构建 Widget 树
- 返回要显示的 UI
- 描述 Widget 的视觉外观
**特点**:
- **可能被多次调用**(这是正常的)
- 应该是**纯函数**:相同的输入产生相同的输出
- 不应该有副作用(如修改全局状态、发起网络请求等)
- 应该快速执行,避免耗时操作
**示例**:
```dart
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('计数: $_count'),
ElevatedButton(
onPressed: () {
setState(() {
_count++;
});
// setState 会触发 build() 再次调用
},
child: Text('增加'),
),
],
),
),
);
}
```
**性能优化建议**:
- 避免在 `build()` 中创建大量对象
- 使用 `const` 构造函数减少重建
- 将复杂的 Widget 提取为独立的 Widget
- 使用 `Builder` 或回调函数延迟构建
**常见错误**:
```dart
// ❌ 错误:在 build() 中执行耗时操作
@override
Widget build(BuildContext context) {
fetchDataFromNetwork(); // 不要这样做!
return Container();
}
// ✅ 正确:在 initState() 或其他生命周期方法中执行
@override
void initState() {
super.initState();
fetchDataFromNetwork();
}
```
### 6. didUpdateWidget()
**调用时机**:当父 Widget 重建,传入新的 Widget 配置时
**用途**:
- 比较新旧 Widget 的配置
- 根据配置变化更新状态
- 决定是否需要重新初始化某些资源
**特点**:
- 在 `build()` 之前调用
- 接收旧的 Widget 作为参数(通过 `widget` 访问新的)
- 可以调用 `setState()`
**示例**:
```dart
class _MyWidgetState extends State<MyWidget> {
late String _currentTitle;
@override
void initState() {
super.initState();
_currentTitle = widget.title;
}
@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// 比较新旧配置
if (oldWidget.title != widget.title) {
// 标题变化了,更新状态
setState(() {
_currentTitle = widget.title;
});
// 可能需要重新加载数据
_loadDataForNewTitle();
}
if (oldWidget.count != widget.count) {
// 计数变化了,执行相应逻辑
}
}
void _loadDataForNewTitle() {
// 根据新标题加载数据
}
}
```
**最佳实践**:
- 始终调用 `super.didUpdateWidget(oldWidget)`
- 只处理真正变化的配置
- 避免不必要的 `setState()` 调用
- 如果配置变化需要重新初始化,考虑在 `didUpdateWidget()` 中处理
## 五、销毁阶段详解
### 7. dispose()
**调用时机**:Widget 从 Widget 树中永久移除时
**用途**:
- 清理资源
- 取消订阅
- 停止定时器
- 释放控制器
- 关闭流
**特点**:
- **只调用一次**(在整个生命周期中)
- 在 Widget 被销毁前调用
- 不能再调用 `setState()`
- 不能再访问 `BuildContext`
**示例**:
```dart
class _MyWidgetState extends State<MyWidget> {
late TextEditingController _controller;
StreamSubscription? _subscription;
Timer? _timer;
AnimationController? _animationController;
@override
void initState() {
super.initState();
_controller = TextEditingController();
_subscription = someStream.listen((data) {});
_timer = Timer.periodic(Duration(seconds: 1), (timer) {});
_animationController = AnimationController(vsync: this);
}
@override
void dispose() {
// 释放控制器
_controller.dispose();
// 取消订阅
_subscription?.cancel();
// 停止定时器
_timer?.cancel();
// 释放动画控制器
_animationController?.dispose();
// 清理其他资源
_cleanupResources();
// 最后调用 super.dispose()
super.dispose();
}
void _cleanupResources() {
// 其他清理逻辑
}
}
```
**必须清理的资源**:
- `TextEditingController` → `dispose()`
- `AnimationController` → `dispose()`
- `StreamSubscription` → `cancel()`
- `Timer` → `cancel()`
- `FocusNode` → `dispose()`
- `ScrollController` → `dispose()`
- 其他需要手动释放的资源
**最佳实践**:
- 始终调用 `super.dispose()`(在最后)
- 清理所有在 `initState()` 中创建的资源
- 使用空安全操作符(`?.`)避免空指针异常
- 不要在 `dispose()` 中调用 `setState()`
**常见错误**:
```dart
// ❌ 错误:忘记清理资源
@override
void dispose() {
// 忘记调用 _controller.dispose()
super.dispose();
}
// ✅ 正确:清理所有资源
@override
void dispose() {
_controller.dispose();
_subscription?.cancel();
super.dispose();
}
```
## 六、完整生命周期示例
下面是一个完整的示例,展示所有生命周期方法的使用:
```dart
class LifecycleDemo extends StatefulWidget {
final String title;
const LifecycleDemo({
Key? key,
required this.title,
}) : super(key: key);
@override
State<LifecycleDemo> createState() => _LifecycleDemoState();
}
class _LifecycleDemoState extends State<LifecycleDemo> {
int _counter = 0;
late TextEditingController _controller;
StreamSubscription? _subscription;
Timer? _timer;
String? _themeMode;
// 1. 构造函数(在 StatefulWidget 中)
// const LifecycleDemo({...}) : super(key: key);
// 2. createState()(在 StatefulWidget 中)
// State<LifecycleDemo> createState() => _LifecycleDemoState();
// 3. initState() - 只调用一次
@override
void initState() {
super.initState();
print('3. initState() 被调用');
_controller = TextEditingController();
_counter = 0;
// 订阅数据流
_subscription = Stream.periodic(
Duration(seconds: 2),
(count) => count,
).listen((count) {
print('Stream 数据: $count');
});
// 启动定时器
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
print('定时器触发: ${timer.tick}');
});
}
// 4. didChangeDependencies() - 依赖变化时调用
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('4. didChangeDependencies() 被调用');
// 访问依赖
final theme = Theme.of(context);
_themeMode = theme.brightness.toString();
final mediaQuery = MediaQuery.of(context);
print('屏幕宽度: ${mediaQuery.size.width}');
}
// 5. build() - 每次 setState 都会调用
@override
Widget build(BuildContext context) {
print('5. build() 被调用');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('计数器: $_counter'),
Text('主题模式: $_themeMode ?? "未知"'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
setState(() {
_counter++;
});
// setState 会触发 build() 再次调用
},
child: Text('增加计数'),
),
],
),
),
);
}
// 6. didUpdateWidget() - 父组件更新时调用
@override
void didUpdateWidget(LifecycleDemo oldWidget) {
super.didUpdateWidget(oldWidget);
print('6. didUpdateWidget() 被调用');
if (oldWidget.title != widget.title) {
print('标题从 "${oldWidget.title}" 变为 "${widget.title}"');
}
}
// 7. dispose() - 只调用一次
@override
void dispose() {
print('7. dispose() 被调用');
// 清理资源
_controller.dispose();
_subscription?.cancel();
_timer?.cancel();
super.dispose();
}
}
```
**控制台输出示例**:
```
3. initState() 被调用
4. didChangeDependencies() 被调用
5. build() 被调用
定时器触发: 1
Stream 数据: 0
定时器触发: 2
5. build() 被调用 // setState 触发
定时器触发: 3
Stream 数据: 1
...
7. dispose() 被调用
```
## 七、常见问题和注意事项
### 1. 生命周期方法的调用顺序
**正确的调用顺序**:
```
构造函数 → createState() → initState() → didChangeDependencies() → build()
↓
setState()
↓
build()
↓
didUpdateWidget()
↓
build()
↓
dispose()
```
### 2. 常见错误和避免方法
#### 错误 1:在 build() 中执行耗时操作
```dart
// ❌ 错误
@override
Widget build(BuildContext context) {
fetchDataFromNetwork(); // 每次 build 都会执行!
return Container();
}
// ✅ 正确
@override
void initState() {
super.initState();
fetchDataFromNetwork(); // 只执行一次
}
```
#### 错误 2:忘记清理资源
```dart
// ❌ 错误
@override
void initState() {
super.initState();
_controller = TextEditingController();
_subscription = stream.listen((data) {});
}
@override
void dispose() {
// 忘记清理!
super.dispose();
}
// ✅ 正确
@override
void dispose() {
_controller.dispose();
_subscription?.cancel();
super.dispose();
}
```
#### 错误 3:在 initState() 中访问依赖
```dart
// ❌ 错误
@override
void initState() {
super.initState();
final theme = Theme.of(context); // 可能为 null!
}
// ✅ 正确
@override
void didChangeDependencies() {
super.didChangeDependencies();
final theme = Theme.of(context); // 安全访问
}
```
#### 错误 4:在 dispose() 后访问资源
```dart
// ❌ 错误
@override
void dispose() {
super.dispose();
_controller.text = 'test'; // 错误!dispose 后不能再使用
}
// ✅ 正确
@override
void dispose() {
_controller.dispose(); // 先清理
super.dispose();
}
```
### 3. 性能优化建议
#### 建议 1:使用 const 构造函数
```dart
// ✅ 好的做法
const Text('Hello')
// ❌ 避免
Text('Hello') // 每次 build 都创建新实例
```
#### 建议 2:提取复杂的 Widget
```dart
// ✅ 好的做法
@override
Widget build(BuildContext context) {
return Column(
children: [
_buildHeader(),
_buildContent(),
_buildFooter(),
],
);
}
Widget _buildHeader() {
return Container(/* ... */);
}
```
#### 建议 3:避免在 build() 中创建大量对象
```dart
// ❌ 避免
@override
Widget build(BuildContext context) {
return ListView(
children: List.generate(1000, (index) {
return Text('Item $index'); // 每次都创建新列表
}),
);
}
// ✅ 优化
final _items = List.generate(1000, (index) => 'Item $index');
@override
Widget build(BuildContext context) {
return ListView(
children: _items.map((item) => Text(item)).toList(),
);
}
```
#### 建议 4:合理使用 setState()
```dart
// ❌ 避免频繁调用
void updateCounter() {
for (int i = 0; i < 100; i++) {
setState(() {
_counter = i; // 触发 100 次 rebuild
});
}
}
// ✅ 优化
void updateCounter() {
setState(() {
_counter = 100; // 只触发一次 rebuild
});
}
```
### 4. 生命周期方法总结表
| 方法 | 调用次数 | 调用时机 | 可以 setState | 可以访问依赖 |
|------|---------|---------|--------------|-------------|
| 构造函数 | 1次 | 创建 Widget 时 | ❌ | ❌ |
| createState() | 1次 | 构造函数之后 | ❌ | ❌ |
| initState() | 1次 | createState() 之后 | ⚠️ 不推荐 | ❌ |
| didChangeDependencies() | 1+次 | initState() 之后,依赖变化时 | ✅ | ✅ |
| build() | 多次 | setState() 或父组件更新时 | ❌ | ✅ |
| didUpdateWidget() | 0+次 | 父组件更新时 | ✅ | ✅ |
| dispose() | 1次 | Widget 销毁时 | ❌ | ❌ |
## 八、总结
理解 Flutter Widget 生命周期是成为优秀 Flutter 开发者的基础。记住以下关键点:
1. **创建阶段**:在 `initState()` 中初始化,在 `didChangeDependencies()` 中访问依赖
2. **运行阶段**:`build()` 应该是纯函数,避免副作用
3. **销毁阶段**:在 `dispose()` 中清理所有资源
4. **性能优化**:使用 const、提取 Widget、避免不必要的重建
5. **最佳实践**:始终调用 `super` 方法,正确处理资源清理
通过合理使用生命周期方法,你可以构建出高效、稳定、易维护的 Flutter 应用。