Flutter 页面生命周期超全总结(附 addPostFrameCallback 详解)

本文涵盖了 Flutter 中 StatefulWidget 的生命周期、App 生命周期以及特殊的 addPostFrameCallback 回调,帮你彻底搞懂页面从创建到销毁的整个过程。

一、概述

Flutter 中的生命周期主要分为三类:

  1. Widget 自身生命周期 - StatefulWidget 的状态变化
  2. App 生命周期 - 整个应用的前后台切换
  3. 帧回调生命周期 - 渲染完成后的特殊回调

二、StatefulWidget 生命周期详解

2.1 生命周期流程图

复制代码
创建页面
   ↓
createState()  
   ↓
initState()  ←─────┐
   ↓               │
didChangeDependencies()
   ↓               │
build()  ←────┐    │
   ↓          │    │
didUpdateWidget()  │
   ↓          │    │
setState() ───┘    │
   ↓               │
deactivate()        │
   ↓               │
dispose()  ←────────┘

2.2 各阶段详细说明

创建阶段

dart 复制代码
class MyPage extends StatefulWidget {
  @override
  _MyPageState createState() => _MyPageState();  // 1. 创建State
}

class _MyPageState extends State<MyPage> {
  @override
  void initState() {  // 2. 初始化,只调用一次
    super.initState();
    print('页面初始化');
    // 初始化数据、添加监听器等
  }

  @override
  void didChangeDependencies() {  // 3. 依赖变化时调用
    super.didChangeDependencies();
    print('依赖变化');
    // 依赖的InheritedWidget变化时触发
  }

  @override
  Widget build(BuildContext context) {  // 4. 构建UI
    print('构建页面');
    return Container();
  }
}

运行阶段

dart 复制代码
@override
void didUpdateWidget(MyPage oldWidget) {  // 父组件重建时调用
  super.didUpdateWidget(oldWidget);
  print('组件更新');
}

void _refreshData() {
  setState(() {  // 手动触发UI更新
    print('数据更新');
  });
}

销毁阶段

dart 复制代码
@override
void deactivate() {  // 从组件树移除时调用
  super.deactivate();
  print('页面即将移除');
}

@override
void dispose() {  // 永久销毁,只调用一次
  print('页面销毁');
  // 释放资源、移除监听
  super.dispose();
}

三、App 生命周期

3.1 使用方式

需要混入 WidgetsBindingObserver:

dart 复制代码
class _MyPageState extends State<MyPage> with WidgetsBindingObserver {
  
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);  // 添加监听
  }
  
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    
    switch (state) {
      case AppLifecycleState.resumed:
        print('应用可见并获取焦点');
        break;
      case AppLifecycleState.inactive:
        print('应用处于非激活状态(如来电)');
        break;
      case AppLifecycleState.paused:
        print('应用进入后台');
        break;
      case AppLifecycleState.detached:
        print('应用已分离');
        break;
    }
  }
  
  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);  // 移除监听
    super.dispose();
  }
}

3.2 状态切换场景

操作 状态变化

打开App detached → resumed

切换到后台 resumed → paused

从后台切回 paused → resumed

来电话 resumed → inactive → paused

挂断电话 paused → inactive → resumed

四、特殊回调:addPostFrameCallback

4.1 是什么?

在当前渲染帧完成后执行的回调,确保在页面完全渲染后执行某些操作。

4.2 执行时机

dart 复制代码
@override
void initState() {
  super.initState();
  print('1. initState');
  
  WidgetsBinding.instance.addPostFrameCallback((_) {
    print('3. 第一帧渲染完成');
  });
  
  print('2. initState结束');
}

@override
Widget build(BuildContext context) {
  print('build执行');
  return Container();
}

// 输出顺序:
// initState
// initState结束
// build执行
// 第一帧渲染完成

4.3 常用场景

场景一:页面加载完成后的初始化

dart 复制代码
@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    // 页面完全渲染后再加载数据
    _loadInitialData();
    _showGuideDialog();
  });
}

场景二:获取组件尺寸

dart 复制代码
GlobalKey _key = GlobalKey();

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    RenderBox box = _key.currentContext?.findRenderObject() as RenderBox;
    Size size = box.size;  // 此时才能获取到正确尺寸
    print('组件尺寸: $size');
  });
}

场景三:避免 build 中的无限循环

dart 复制代码
// ❌ 错误示范
@override
Widget build(BuildContext context) {
  setState(() {});  // 会导致无限循环
  return Container();
}

// ✅ 正确示范
@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    setState(() {});  // 只在第一帧后执行一次
  });
}

场景四:页面曝光埋点

dart 复制代码
@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    _reportPageView();  // 页面完全显示后才上报曝光
  });
}

4.4 注意事项

  1. 不需要手动移除:回调执行后自动清理
  2. 可添加多个:按添加顺序执行
  3. 只在当前帧执行:如需每帧执行,使用 addPersistentFrameCallback

五、完整场景示例

5.1 综合应用

dart 复制代码
class LifecycleDemoPage extends StatefulWidget {
  @override
  _LifecycleDemoPageState createState() => _LifecycleDemoPageState();
}

class _LifecycleDemoPageState extends State<LifecycleDemoPage> 
    with WidgetsBindingObserver {
  
  bool _isLoading = true;
  List<String> _data = [];
  final GlobalKey _listKey = GlobalKey();
  
  @override
  void initState() {
    super.initState();
    print('📝 initState: 页面初始化');
    
    // 添加App生命周期监听
    WidgetsBinding.instance.addObserver(this);
    
    // 页面渲染完成后执行
    WidgetsBinding.instance.addPostFrameCallback((_) {
      print('🎨 第一帧渲染完成');
      _onFirstFrameRendered();
    });
  }
  
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('📝 didChangeDependencies: 依赖变化');
  }
  
  @override
  void didUpdateWidget(LifecycleDemoPage oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('📝 didUpdateWidget: 组件更新');
  }
  
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    print('📱 App状态: $state');
    
    if (state == AppLifecycleState.resumed) {
      // 从后台切回时刷新数据
      _refreshData();
    }
  }
  
  void _onFirstFrameRendered() {
    // 获取列表位置信息
    final box = _listKey.currentContext?.findRenderObject() as RenderBox;
    print('📏 列表位置: ${box.localToGlobal(Offset.zero)}');
    
    // 加载数据
    _loadData();
  }
  
  Future<void> _loadData() async {
    setState(() => _isLoading = true);
    
    // 模拟网络请求
    await Future.delayed(Duration(seconds: 1));
    
    if (mounted) {
      setState(() {
        _data = List.generate(20, (i) => 'Item $i');
        _isLoading = false;
      });
      
      // 数据加载完成后的曝光上报
      WidgetsBinding.instance.addPostFrameCallback((_) {
        print('📊 数据加载完成,可以上报曝光');
      });
    }
  }
  
  void _refreshData() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      print('🔄 刷新数据');
      _loadData();
    });
  }
  
  @override
  void deactivate() {
    super.deactivate();
    print('📝 deactivate: 页面即将移除');
  }
  
  @override
  void dispose() {
    print('📝 dispose: 页面销毁');
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    print('🎨 build: 构建页面');
    
    return Scaffold(
      appBar: AppBar(title: Text('生命周期示例')),
      body: _isLoading
          ? Center(child: CircularProgressIndicator())
          : ListView.builder(
              key: _listKey,
              itemCount: _data.length,
              itemBuilder: (ctx, i) => ListTile(title: Text(_data[i])),
            ),
    );
  }
}

5.2 常见场景速查表

需求 使用的方法

页面初始化数据 initState

页面渲染后操作 addPostFrameCallback

获取组件尺寸 addPostFrameCallback

从后台切回刷新 didChangeAppLifecycleState + resumed

避免重复build addPostFrameCallback + setState

页面曝光埋点 addPostFrameCallback

释放资源 dispose

移除监听 dispose

六、总结

6.1 执行顺序总结

复制代码
第一次进入页面:
initState → didChangeDependencies → build → addPostFrameCallback → resumed

页面切换(A→B):
A: deactivate
B: initState → build → resumed

从B返回A:
A: build → resumed

App退到后台:
resumed → paused

App从后台返回:
paused → resumed

6.2 最佳实践建议

  1. initState:只做轻量初始化,不要调用 setState
  2. build:只做UI构建,不要做耗时操作
  3. addPostFrameCallback:适合做渲染后的操作、数据加载、埋点
  4. dispose:必须释放所有资源,移除监听
  5. didChangeAppLifecycleState:处理前后台切换逻辑

💡 记住要点:

· initState:出生

· build:成长

· addPostFrameCallback:成年(可以做事了)

· dispose:消亡

· resumed:睡醒(回到前台)

希望这篇总结对你有帮助!如有遗漏或错误,欢迎指正交流。

相关推荐
国医中兴3 小时前
Flutter 三方库 dson 的鸿蒙化适配指南 - 极简的序列化魔法、在鸿蒙端实现反射式 JSON 映射实战
flutter·harmonyos·鸿蒙·openharmony
Lesile4 小时前
Flutter回顾#1:动画:
flutter
yuanlaile4 小时前
2026年全新版Flutter教程_Dart Flutter入门实战系列视频教程
flutter·flutter教程·dart教程·flutter必备基础
国医中兴6 小时前
Flutter 三方库 cloudflare_r2_uploader 的鸿蒙化适配指南 - 云端存储的疾速通道、在鸿蒙端实现 R2 分段上传实战
flutter·harmonyos·鸿蒙·openharmony·cloudflare_r2_uploader
恋猫de小郭6 小时前
Flutter Beta 版本引入 ScrollCacheExtent ,并修复长久存在的 shrinkWrap NaN 问题
android·前端·flutter
国医中兴6 小时前
Flutter 三方库 weaver 的鸿蒙化适配指南 - 玩转轻量级服务发现、在鸿蒙端实现模块化治理与解构实战
flutter·harmonyos·鸿蒙·openharmony
国医中兴6 小时前
Flutter 三方库 tapper 的鸿蒙化适配指南 - 单元测试的“闪电侠”、在鸿蒙端实现极简函数式测试实战
flutter·harmonyos·鸿蒙·openharmony
国医中兴19 小时前
Flutter 三方库 stack_blur 鸿蒙适配指南 - 实现工业级高性能模糊滤镜、在 OpenHarmony 上打造极致视觉质感实战
flutter·华为·harmonyos
Justin在掘金20 小时前
Flutter Provider 状态管理深度指南
flutter