From 811a8cc70d0b2daf6bb4479a8fcbf106f3078da0 Mon Sep 17 00:00:00 2001 From: Ishaq Hassan Date: Wed, 21 Oct 2026 07:49:12 +0000 Subject: [PATCH 1/2] Fix stale "no timeline event analysis data" message in Frame Analysis tab The selected frame is published before its timeline events are loaded. When the events have to be pulled from the VM they are assigned to the same FlutterFrame instance later, so selectedFrame never fires again and the Frame Analysis tab keeps showing the no-data message. Notify selectedFrame listeners again when events are assigned to the selected frame. Fixes #9729 --- .../flutter_frames_controller.dart | 23 +++++++++++- .../release_notes/NEXT_RELEASE_NOTES.md | 5 ++- .../flutter_frames_controller_test.dart | 37 +++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart index 309b5c7b4a4..8930bfe2181 100644 --- a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart +++ b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart @@ -22,7 +22,7 @@ class FlutterFramesController extends PerformanceFeatureController { /// The currently selected timeline frame. ValueListenable get selectedFrame => _selectedFrameNotifier; - final _selectedFrameNotifier = ValueNotifier(null); + final _selectedFrameNotifier = _SelectedFrameNotifier(null); /// The flutter frames in the chart. ValueListenable> get flutterFrames => _flutterFrames; @@ -123,6 +123,12 @@ class FlutterFramesController extends PerformanceFeatureController { _unassignedFlutterFrames.remove(frameNumber); _updateFirstWellFormedFrameMicros(frame); } + if (_selectedFrameNotifier.value == frame) { + // [frame] was selected before its timeline events were available. Notify + // listeners again so that views built from [selectedFrame], like the + // Frame Analysis tab, rebuild with the data that is now available. + _selectedFrameNotifier.notifyListeners(); + } } bool hasUnassignedFlutterFrame(int frameNumber) { @@ -223,3 +229,18 @@ class FlutterFramesController extends PerformanceFeatureController { super.dispose(); } } + +/// Notifier for the selected [FlutterFrame]. +/// +/// Timeline event data can be assigned to a [FlutterFrame] after the frame has +/// been selected, so listeners need to be notified even when the selected +/// [FlutterFrame] instance itself has not changed. +class _SelectedFrameNotifier extends ValueNotifier { + _SelectedFrameNotifier(super.value); + + @override + // This override is needed to change visibility of the method. + void notifyListeners() { + super.notifyListeners(); + } +} diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index 7d47c6b982a..6302a217336 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -24,7 +24,10 @@ TODO: Remove this section if there are not any updates. ## Performance updates -TODO: Remove this section if there are not any updates. +* Fixed the Frame Analysis tab showing "No timeline event analysis data + available for this frame" when a frame is selected before its timeline events + have been fetched from the VM. + [#9729](https://github.com/flutter/devtools/issues/9729) ## CPU profiler updates diff --git a/packages/devtools_app/test/screens/performance/flutter_frames/flutter_frames_controller_test.dart b/packages/devtools_app/test/screens/performance/flutter_frames/flutter_frames_controller_test.dart index 1448a26f2be..fd5a718c7a0 100644 --- a/packages/devtools_app/test/screens/performance/flutter_frames/flutter_frames_controller_test.dart +++ b/packages/devtools_app/test/screens/performance/flutter_frames/flutter_frames_controller_test.dart @@ -87,6 +87,43 @@ void main() { expect(framesController.selectedFrame.value, equals(FlutterFrame4.frame)); }); + test('notifies listeners when events are assigned to selected frame', () { + when( + // ignore: discarded_futures, by design + mockTimelineEventsController.handleSelectedFrame(any), + ).thenAnswer((_) => Future.value()); + + final frame = FlutterFrame.fromJson({ + 'number': 3, + 'startTime': 40000, + 'elapsed': 20000, + 'build': 16000, + 'raster': 16000, + 'vsyncOverhead': 1000, + }); + framesController.addFrame(frame); + framesController.handleSelectedFrame(frame); + expect(frame.frameAnalysis, isNull); + + var notifyCount = 0; + framesController.selectedFrame.addListener(() => notifyCount++); + + // Assigning an event to the selected frame should notify listeners, even + // though the selected frame instance has not changed. + framesController.assignEventToFrame(frame.id, FlutterFrame2.uiEvent); + expect(notifyCount, equals(1)); + expect(framesController.selectedFrame.value, equals(frame)); + expect(frame.frameAnalysis, isNotNull); + + // Assigning an event to a frame that is not selected should not notify. + framesController.addFrame(testFrame2); + framesController.assignEventToFrame( + testFrame2.id, + FlutterFrame2.rasterEvent, + ); + expect(notifyCount, equals(1)); + }); + test('can setOfflineData', () async { // Ensure we are starting in an empty state. expect(framesController.flutterFrames.value, isEmpty); From 7f8931bc105b0f182ddb9c95cddec0074bada7b4 Mon Sep 17 00:00:00 2001 From: Ishaq Hassan Date: Thu, 22 Oct 2026 02:02:06 +0500 Subject: [PATCH 2/2] Use a named method to notify selected frame listeners Replace the notifyListeners visibility override in _SelectedFrameNotifier with notifyFrameDataChanged, and document why the subclass is needed. Also link the release note entry to the PR. --- .../flutter_frames_controller.dart | 16 +++++++--------- .../release_notes/NEXT_RELEASE_NOTES.md | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart index 8930bfe2181..63ee814e46b 100644 --- a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart +++ b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart @@ -127,7 +127,7 @@ class FlutterFramesController extends PerformanceFeatureController { // [frame] was selected before its timeline events were available. Notify // listeners again so that views built from [selectedFrame], like the // Frame Analysis tab, rebuild with the data that is now available. - _selectedFrameNotifier.notifyListeners(); + _selectedFrameNotifier.notifyFrameDataChanged(); } } @@ -232,15 +232,13 @@ class FlutterFramesController extends PerformanceFeatureController { /// Notifier for the selected [FlutterFrame]. /// -/// Timeline event data can be assigned to a [FlutterFrame] after the frame has -/// been selected, so listeners need to be notified even when the selected -/// [FlutterFrame] instance itself has not changed. +/// Timeline events are assigned to a [FlutterFrame] in place, which can happen +/// after the frame has been selected. [ValueNotifier] only notifies when +/// [value] changes, and [notifyListeners] is protected, so this subclass +/// exposes [notifyFrameDataChanged] for that case. class _SelectedFrameNotifier extends ValueNotifier { _SelectedFrameNotifier(super.value); - @override - // This override is needed to change visibility of the method. - void notifyListeners() { - super.notifyListeners(); - } + /// Notifies listeners that the selected frame's data changed in place. + void notifyFrameDataChanged() => notifyListeners(); } diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index 6302a217336..92366f83803 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -27,7 +27,7 @@ TODO: Remove this section if there are not any updates. * Fixed the Frame Analysis tab showing "No timeline event analysis data available for this frame" when a frame is selected before its timeline events have been fetched from the VM. - [#9729](https://github.com/flutter/devtools/issues/9729) + [#10050](https://github.com/flutter/devtools/pull/10050) ## CPU profiler updates