collectFromFrameTimings method

Future<List<FrameData>> collectFromFrameTimings(
  1. VmService service,
  2. String isolateId,
  3. Duration window
)

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;
}