Skip to content

生命周期

理解 StatefulWidget 的生命周期,是写出正确、高效 Flutter 代码的关键。

生命周期流程图

                ┌──────────────┐
  创建 Widget   │  createState │
  ────────────► │              │
                └──────┬───────┘


                ┌──────────────┐
                │  initState   │  ← 只调用一次
                │  (初始化)     │
                └──────┬───────┘


                ┌──────────────────┐
                │didChangeDependencies│  ← 依赖变化时
                └──────┬───────────┘

          ┌────────────┴────────────┐
          ▼                         ▼
   ┌────────────┐          ┌─────────────┐
   │   build    │◄─────────│ didUpdateWidget │
   │  (构建UI)   │  属性变化  │ (属性更新时)   │
   └──────┬─────┘          └─────────────┘


   ┌────────────┐    setState()
   │   build    │◄──────────
   │  (重建UI)   │   状态变化
   └──────┬─────┘


   ┌────────────┐
   │ deactivate │  ← 从树中移除(可能被重新插入)
   └──────┬─────┘

    被丢弃? ── 是 ──►  ┌──────────┐
          │             │ dispose  │  ← 永久销毁
          否            └──────────┘

          └─── 重新插入树 ──►  回到 build()

各阶段详解

initState()

dart
@override
void initState() {
  super.initState();
  // ✅ 在这里做初始化工作
  _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 1),
  );
  _loadData();
}

适用场景:

  • 初始化控制器(AnimationController、TextEditingController、ScrollController)
  • 初始化状态变量的初始值
  • 订阅事件流(Stream)
  • 发起首次网络请求
  • 添加监听器

注意:

  • 不能在这里调用 BuildContext 相关方法(如 Theme.of(context)),此时 context 还未完全就绪
  • 需要 context 的初始化操作请放在 didChangeDependencies()

didChangeDependencies()

dart
@override
void didChangeDependencies() {
  super.didChangeDependencies();
  // ✅ 在这里使用 context 依赖的值
  final size = MediaQuery.of(context).size;
  final theme = Theme.of(context);
}

适用场景:

  • 获取 MediaQueryThemeInheritedWidget 的值
  • 当这些依赖发生变化时会重新调用

build()

dart
@override
Widget build(BuildContext context) {
  return Container(
    child: Text('count: $_count'),
  );
}

适用场景:

  • 根据当前状态构建 UI
  • 每次 setState() 后都会被调用
  • 尽量保持纯函数,避免副作用

didUpdateWidget()

dart
@override
void didUpdateWidget(covariant MyWidget oldWidget) {
  super.didUpdateWidget(oldWidget);
  // ✅ 父 Widget 传入新属性时的处理
  if (widget.value != oldWidget.value) {
    _controller.animateTo(widget.value);
  }
}

适用场景:

  • 当父组件传入的属性变化时,同步更新内部状态
  • 比较新旧属性 widget.xxx != oldWidget.xxx

deactivate()

dart
@override
void deactivate() {
  super.deactivate();
  // 通常不需要做什么
}

说明:

  • 当 State 对象从 Widget 树中移除时调用
  • 可能会被重新插入(比如从一个位置移到另一个位置)
  • 通常不需要重写这个方法

dispose()

dart
@override
void dispose() {
  // ✅ 必须释放所有资源!
  _controller.dispose();
  _textEditingController.dispose();
  _scrollController.dispose();
  _subscription.cancel();
  super.dispose();
}

适用场景:

  • 释放所有控制器(AnimationController、TextEditingController 等)
  • 取消订阅(StreamSubscription、ChangeNotifier)
  • 取消定时器
  • 移除监听器

重要

忘记调用 dispose() 会导致内存泄漏!只要在 initState() 中创建/订阅了资源,就必须在 dispose() 中释放/取消。

常见模式

带网络请求的 Widget

dart
class UserList extends StatefulWidget {
  const UserList({super.key});

  @override
  State<UserList> createState() => _UserListState();
}

class _UserListState extends State<UserList> {
  List<User> _users = [];
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _fetchUsers();  // 初始化时加载数据
  }

  Future<void> _fetchUsers() async {
    try {
      final users = await ApiService.getUsers();
      // ─── ★ 异步操作后检查 mounted ──────────────
      if (!mounted) return;
      // ─── ☆ 异步操作后检查 mounted ──────────────
      setState(() {
        _users = users;
        _isLoading = false;
      });
    } catch (e) {
      if (!mounted) return;
      setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return const CircularProgressIndicator();
    }
    return ListView.builder(
      itemCount: _users.length,
      itemBuilder: (context, index) {
        return ListTile(title: Text(_users[index].name));
      },
    );
  }
}

带动画的 Widget

dart
class FadeIn extends StatefulWidget {
  const FadeIn({super.key, required this.child});

  final Widget child;

  @override
  State<FadeIn> createState() => _FadeInState();
}

class _FadeInState extends State<FadeIn> with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,       // SingleTickerProviderStateMixin 提供 vsync
      duration: const Duration(milliseconds: 500),
    );
    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.easeIn,
    );
    _controller.forward();  // 启动动画
  }

  @override
  void dispose() {
    _controller.dispose();  // 释放控制器
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _animation,
      child: widget.child,
    );
  }
}

mounted 检查

在异步操作后调用 setState 前,检查组件是否还在 Widget 树中:

dart
import 'package:flutter/material.dart';

class MountedCheckPage extends StatefulWidget {
  const MountedCheckPage({super.key});

  @override
  State<MountedCheckPage> createState() => _MountedCheckPageState();
}

class _MountedCheckPageState extends State<MountedCheckPage> {
  String _data = '等待加载';

  void _fetchData() async {
    final result = await Future.delayed(const Duration(seconds: 2));
    if (!mounted) return;   // 组件已被销毁
    setState(() => _data = '结果: $result');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('mounted 检查')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(_data, style: const TextStyle(fontSize: 20)),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _fetchData,
              child: const Text('加载数据'),
            ),
          ],
        ),
      ),
    );
  }
}

mountedState 类的属性,表示当前 State 是否还在 Widget 树中。在异步回调中修改状态前,务必检查 mounted

生命周期速查

方法调用时机用途
initState()插入树时,仅一次初始化控制器、订阅、网络请求
didChangeDependencies()依赖变化时获取 MediaQuery、Theme 等
build()每次需要渲染时构建 UI
didUpdateWidget()父 Widget 重建时响应属性变化
deactivate()从树中移除时通常不需要
dispose()永久销毁时释放所有资源

下一步

基于 Flutter 官方文档整理