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();
      setState(() {
        _users = users;
        _isLoading = false;
      });
    } catch (e) {
      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,
    );
  }
}

下一步

基于 Flutter 官方文档整理