SchedulerBinding 的三个Frame回调

addPersistentFrameCallbackaddPostFrameCallbackaddTimingsCallback 确实都是 Flutter 框架调度中"Frame 级别"的回调,但三者触发时机完全不同, 它们分别连接着 Frame 生命周期的三个阶段。 一个详细、专业但清晰的对照和时间线讲解 👇


🧭 一、概览表

回调接口 注册位置 执行时机(帧流程阶段) 回调线程 常见用途 是否每帧持续触发
addPersistentFrameCallback SchedulerBinding 在每帧开始时,绘制流程开始之前(布局、绘制前) UI Thread(Dart isolate) 自定义动画、同步逻辑、监控 frame build ✅ 是,每帧都会触发
addPostFrameCallback WidgetsBinding 帧绘制完成后、但在下一帧开始前(UI 树渲染结束) UI Thread(Dart isolate) 在 build 完后执行一次性逻辑(如状态恢复、测量布局等) ❌ 否,只执行一次
addTimingsCallback SchedulerBinding 一帧真正完成后(包括 rasterizer 渲染、GPU 提交) Engine ➜ Dart isolate 性能分析、帧率统计、掉帧检测 ⚙️ 仅在帧真正渲染完成时触发(由 Engine 主动回调)

🧩 二、完整帧生命周期时间线图

下面是一帧从触发到结束的时间轴:

scss 复制代码
┌──────────────────────────────────────────────────────────────┐
│ 一帧的完整生命周期(Flutter Pipeline)                       │
├──────────────────────────────────────────────────────────────┤
│ vsync 信号到达 → 触发 Scheduler.handleBeginFrame             │
│      ↓                                                       │
│ ① addPersistentFrameCallback() 触发                          │ ← 每帧都会调用(UI逻辑)
│      ↓                                                       │
│ Widgets build/rebuild/layout/paint 完成                      │
│      ↓                                                       │
│ ② addPostFrameCallback() 执行                                │ ← 本帧后执行一次(UI在屏幕前执行的最后逻辑)
│      ↓                                                       │
│ UI 层命令发送给 Engine,Rasterizer 开始渲染 → GPU            │
│      ↓                                                       │
│ ③ addTimingsCallback() 触发                                 │ ← Engine 通知 Dart:此帧渲染周期完结
│      ↓                                                       │
│ 下一帧 vsync 周期开始                                         │
└──────────────────────────────────────────────────────────────┘

🧠 三、详细解释

1️⃣ addPersistentFrameCallback

  • 🔹 每当 Flutter engine 安排一次 Frame(vsync 信号触发)时调用;
  • 🔹 在 WidgetsBinding.drawFrame() 之前;
  • 🔹 多用于做动画同步,如 Ticker, AnimationController
  • 🔹 不可在里面直接调用 setState()(会导致无限循环)。
dart 复制代码
SchedulerBinding.instance.addPersistentFrameCallback((Duration timeStamp) {
  // 每帧都会执行一次
  // timeStamp:距离应用启动的时间
});

用途:开发 Frame 驱动逻辑、动画系统、心跳检测。


2️⃣ addPostFrameCallback

  • 🔹 注册一个下一帧绘制完成后执行的回调;
  • 🔹 只执行一次;
  • 🔹 常用于需要在 widget build 完之后去访问 context 或做布局调整。
dart 复制代码
WidgetsBinding.instance.addPostFrameCallback((_) {
  print("这一帧的Widget都build & render完毕之后执行");
});

用途:执行一次性的渲染后逻辑(比如弹框、滚动控制、布局测量)。


3️⃣ addTimingsCallback

  • 🔹 由 Flutter 引擎 在一帧完全渲染完成(包括 GPU 渲染)后回调;
  • 🔹 参数提供本帧的渲染性能信息:
    • build、rasterize 时间、
    • start/finish 时间戳;
  • 🔹 不一定每帧都有(只有当一帧真正提交给屏幕后才会触发)。
dart 复制代码
SchedulerBinding.instance.addTimingsCallback((List<FrameTiming> timings) {
  for (final t in timings) {
    print('build: ${t.buildDuration}, raster: ${t.rasterDuration}');
  }
});

用途:性能监控、丢帧检测、UI心跳。


🧩 四、对应的 Pipeline Phase(官方描述)

Scheduler Phase 回调接口 官方解释
TRANSIENT_CALLBACKS addPersistentFrameCallback 一帧准备阶段,执行持久 frame 回调
POST_FRAME_CALLBACKS addPostFrameCallback 当前帧绘制完毕后执行延迟回调
FrameTimings (engine reported) addTimingsCallback 一帧渲染完成,engine 向 Dart 报告

⚙️ 五、典型使用示例汇总

dart 复制代码
void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // 每帧都会触发 - 可作心跳
  SchedulerBinding.instance.addPersistentFrameCallback((timeStamp) {
    print('Persistent at ${timeStamp.inMilliseconds}');
  });

  // 当前帧绘制完后执行一次
  WidgetsBinding.instance.addPostFrameCallback((_) {
    print('Widget build done.');
  });

  // 渲染完成后(含GPU提交)
  SchedulerBinding.instance.addTimingsCallback((timings) {
    final t = timings.last;
    print('Frame done. build=${t.buildDuration}, raster=${t.rasterDuration}');
  });

  runApp(MyApp());
}

✅ 最终总结表

回调 调用频率 触发时机 所属线程 典型用途
addPersistentFrameCallback 每帧 每帧开始(UI build 前) UI isolate 动画驱动、持续逻辑、心跳
addPostFrameCallback 一次 当前帧 build 后(UI 树构建完) UI isolate 布局调整、逻辑收尾
addTimingsCallback 条件触发 一帧 GPU 渲染完成后 Engine 调用 Dart 性能分析、渲染监控

🔥 一句总结记忆法:

Persistent ------ 每帧都来; Post ------ 帧后执行一次; Timings ------ 一帧真上屏后报告性能。


相关推荐
这是个栗子10 小时前
【问题解决】Vue2 与 Vue3项目中 Node.js 版本选择
前端·node.js·nvm
222you10 小时前
SpringBeanFactory
java·服务器·前端
苏打水com10 小时前
第十一篇:Day31-33 前端安全与性能监控——从“能用”到“安全可靠”(对标职场“系统稳定性”需求)
前端·javascript·css·vue.js·html
fpl111610 小时前
npm :无法加载文件 D:\...\nodejs\npm.ps1,因为在此系统上禁止运行脚本
前端·vscode·npm·node.js·命令模式
kirk_wang10 小时前
Flutter Printing库在OpenHarmony上的适配实战
flutter·移动开发·跨平台·arkts·鸿蒙
LYFlied10 小时前
LeetCode热题Top100:核心算法思想与前端实战套路
前端·算法·leetcode·面试·算法思想·算法套路·解题公式
赵庆明老师10 小时前
NET 中,使用SignalR 调用Controller 控制器的 Hub 方法
前端·html·xhtml
zhangwenwu的前端小站10 小时前
VUE 实现划词 问AI 翻译等功能
前端·javascript·vue.js
黑臂麒麟10 小时前
华为云的DevUI&Form组件实战:个人信息编辑表单完整实现
前端·javascript·ui·华为云·angular.js
程序员小寒10 小时前
前端高频面试题之手写Promise
前端·javascript·面试