collectFromFrameTimings method
Primary method: collect FrameTiming events from flutter.frame extension stream. Flutter emits pre-computed build+raster+vsync breakdown per frame. Much more accurate than parsing raw timeline events.
Implementation
Future<List<FrameData>> collectFromFrameTimings(
VmService service,
String isolateId,
Duration window,
) async {
final frames = <FrameData>[];
await service.streamListen(EventStreams.kExtension).catchError((_) => Success());
// Kick engine to produce frames — needed on Android/emulator when app is idle
await service.callServiceExtension(
'ext.ui.window.scheduleFrame',
isolateId: isolateId,
).catchError((_) => Response());
final sub = service.onExtensionEvent.listen((event) {
if (event.extensionKind != 'Flutter.Frame') return;
final data = event.extensionData?.data ?? event.json;
if (data == null) return;
// Flutter.Frame payload:
// { number, startTime, elapsed, build, raster, vsyncOverhead }
// All times in microseconds
final build = (data['build'] as num?)?.toInt() ?? 0;
final raster = (data['raster'] as num?)?.toInt() ?? 0;
final elapsed = (data['elapsed'] as num?)?.toInt() ??
(build + raster); // elapsed = build + raster + vsyncOverhead
final startTime = (data['startTime'] as num?)?.toInt() ?? 0;
if (elapsed <= 0 && build <= 0) return;
final total = elapsed > 0 ? elapsed : (build + raster);
frames.add(FrameData(
frameNumber: frames.length + 1,
uiStartMicros: startTime,
uiDurationMicros: build,
rasterDurationMicros: raster,
totalDurationMicros: total,
));
});
await Future.delayed(window);
await sub.cancel();
return frames;
}