addPersistentFrameCallback
、addPostFrameCallback
、addTimingsCallback
确实都是 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
------ 一帧真上屏后报告性能。