diff --git a/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart b/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart index a19d2502276..41e64aae318 100644 --- a/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart +++ b/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart @@ -92,6 +92,19 @@ class _RebuildStatsViewState extends State @override Widget build(BuildContext context) { + final isProfileBuild = + serviceConnection.serviceManager.connectedApp?.isProfileBuildNow ?? + false; + if (isProfileBuild) { + return const Center( + child: Text( + 'Rebuild information is not available for this frame.\n' + 'Widget rebuild counts are only available when running ' + 'an app in debug-mode.', + textAlign: TextAlign.center, + ), + ); + } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index 05805dbad6f..98f54297eab 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -3,6 +3,7 @@ Copyright 2025 The Flutter Authors Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. --> + This is a draft for future release notes that are going to land on [the Flutter website](https://docs.flutter.dev/tools/devtools/release-notes). @@ -11,7 +12,7 @@ This is a draft for future release notes that are going to land on The 2.58.0 release of the Dart and Flutter DevTools includes the following changes among other general improvements. To learn more about DevTools, check out the -[DevTools overview](/tools/devtools). +[DevTools overview](https://docs.flutter.dev/tools/devtools). ## General updates @@ -23,7 +24,8 @@ TODO: Remove this section if there are not any updates. ## Performance updates -TODO: Remove this section if there are not any updates. +- Show a message in the Performance panel when widget rebuild tracking is + unavailable because the app is running in profile mode. [#9755](https://github.com/flutter/devtools/pull/9755) ## CPU profiler updates @@ -68,4 +70,4 @@ TODO: Remove this section if there are not any updates. ## Full commit history To find a complete list of changes in this release, check out the -[DevTools git log](https://github.com/flutter/devtools/tree/v2.58.0). +[DevTools git log](https://github.com/flutter/devtools/tree/v2.58.0). \ No newline at end of file diff --git a/packages/devtools_app/test/screens/performance/performance_screen_test.dart b/packages/devtools_app/test/screens/performance/performance_screen_test.dart index 6f23218ee51..e1a440085e1 100644 --- a/packages/devtools_app/test/screens/performance/performance_screen_test.dart +++ b/packages/devtools_app/test/screens/performance/performance_screen_test.dart @@ -9,6 +9,9 @@ import 'dart:async'; import 'package:devtools_app/devtools_app.dart'; import 'package:devtools_app/src/screens/performance/panes/controls/performance_controls.dart'; +import 'package:devtools_app/src/screens/performance/panes/flutter_frames/flutter_frame_model.dart'; +import 'package:devtools_app/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart'; +import 'package:devtools_app/src/screens/performance/panes/rebuild_stats/rebuild_stats_model.dart'; import 'package:devtools_app/src/screens/performance/panes/timeline_events/timeline_events_view.dart'; import 'package:devtools_app/src/screens/performance/tabbed_performance_view.dart'; import 'package:devtools_app/src/shared/feature_flags.dart'; @@ -18,6 +21,7 @@ import 'package:devtools_app_shared/utils.dart'; import 'package:devtools_shared/devtools_test_utils.dart'; import 'package:devtools_test/devtools_test.dart'; import 'package:devtools_test/helpers.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -218,7 +222,6 @@ void main() { await tester.runAsync(() async { await pumpPerformanceScreen(tester, runAsync: true); await tester.pumpAndSettle(); - final chartButtonFinder = find.byType(VisibilityButton); expect(chartButtonFinder, findsOneWidget); @@ -231,7 +234,6 @@ void main() { await tester.tap(chartButtonFinder); await tester.pumpAndSettle(); - // The flutter frames chart should no longer be visible. expect(find.byType(FramesChartControls), findsNothing); expect( @@ -252,46 +254,6 @@ void main() { }, ); - // testWidgetsWithWindowSize( - // 'clears timeline on clear', - // windowSize, - // (WidgetTester tester) async { - // await tester.runAsync(() async { - // await pumpPerformanceScreen(tester, runAsync: true); - // await tester.pumpAndSettle(); - - // // Ensure the Timeline Events tab is selected. - // final timelineEventsTabFinder = find.text('Timeline Events'); - // expect(timelineEventsTabFinder, findsOneWidget); - // await tester.tap(timelineEventsTabFinder); - // await tester.pumpAndSettle(); - - // expect( - // controller.timelineEventsController.allTraceEvents, - // isNotEmpty, - // ); - // expect(find.byType(FlutterFramesChart), findsOneWidget); - // expect(find.byType(TimelineFlameChart), findsOneWidget); - // expect( - // find.byKey(TimelineEventsView.emptyTimelineKey), - // findsNothing, - // ); - // expect(find.byType(EventDetails), findsOneWidget); - - // await tester.tap(find.byIcon(Icons.block)); - // await tester.pumpAndSettle(); - // expect(controller.timelineEventsController.allTraceEvents, isEmpty); - // expect(find.byType(FlutterFramesChart), findsOneWidget); - // expect(find.byType(TimelineFlameChart), findsNothing); - // expect( - // find.byKey(TimelineEventsView.emptyTimelineKey), - // findsOneWidget, - // ); - // expect(find.byType(EventDetails), findsNothing); - // }); - // }, - // ); - testWidgetsWithWindowSize('opens enhance tracing overlay', windowSize, ( WidgetTester tester, ) async { @@ -395,6 +357,69 @@ void main() { }, ); }); + + group('RebuildStatsView', () { + late FakeServiceConnectionManager fakeServiceConnection; + late RebuildCountModel model; + late ValueNotifier selectedFrame; + + setUp(() { + fakeServiceConnection = FakeServiceConnectionManager(); + final app = fakeServiceConnection.serviceManager.connectedApp!; + when(app.initialized).thenReturn(Completer()..complete(true)); + when(app.isDartWebAppNow).thenReturn(false); + when(app.isFlutterAppNow).thenReturn(true); + when(app.isDartCliAppNow).thenReturn(false); + when(app.isDartWebApp).thenAnswer((_) async => false); + when(app.isProfileBuild).thenAnswer((_) async => false); + setGlobal(ServiceConnectionManager, fakeServiceConnection); + setGlobal(IdeTheme, IdeTheme()); + setGlobal(NotificationService, NotificationService()); + setGlobal(BannerMessagesController, BannerMessagesController()); + setGlobal(PreferencesController, PreferencesController()); + setGlobal(OfflineDataController, OfflineDataController()); + model = RebuildCountModel(); + selectedFrame = ValueNotifier(null); + }); + + testWidgets('shows message when running in profile mode', ( + WidgetTester tester, + ) async { + final app = fakeServiceConnection.serviceManager.connectedApp!; + when(app.isProfileBuildNow).thenReturn(true); + + await tester.pumpWidget( + wrapWithControllers( + RebuildStatsView(model: model, selectedFrame: selectedFrame), + ), + ); + await tester.pump(); + + expect( + find.textContaining('Widget rebuild counts are only available'), + findsOneWidget, + ); + }); + + testWidgets('shows normal UI when running in debug mode', ( + WidgetTester tester, + ) async { + final app = fakeServiceConnection.serviceManager.connectedApp!; + when(app.isProfileBuildNow).thenReturn(false); + + await tester.pumpWidget( + wrapWithControllers( + RebuildStatsView(model: model, selectedFrame: selectedFrame), + ), + ); + await tester.pump(); + + expect( + find.textContaining('Widget rebuild counts are only available'), + findsNothing, + ); + }); + }); }); }