Show debug-mode message for widget rebuilds in profile mode (#9755)

When running in profile mode, the Performance panel showed a
disabled 'Count widget builds' checkbox which was misleading.
Widget rebuild counts rely on debugOnRebuildDirtyWidget which
is only available in debug mode.

This change:
- Removes the checkbox when running in profile mode
- Shows a clear message: 'Rebuild information is not available
  for this frame. Widget rebuild counts are only available when
  running an app in debug-mode.'
- Adds widget tests for both profile mode and debug mode behavior

Fixes #9730


---------

Co-authored-by: Sam Rawlins <srawlins@google.com>
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 a19d250..6b7ec33 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
@@ -20,6 +20,12 @@
 import '../flutter_frames/flutter_frame_model.dart';
 import 'rebuild_stats_model.dart';
 
+@visibleForTesting
+const debugModeOnlyMessage =
+    'Rebuild information is not available for this frame.\n'
+    'Widget rebuild counts are only available when running '
+    'an app in debug mode.';
+
 class RebuildStatsView extends StatefulWidget {
   const RebuildStatsView({
     super.key,
@@ -92,6 +98,12 @@
 
   @override
   Widget build(BuildContext context) {
+    final isProfileBuild =
+        serviceConnection.serviceManager.connectedApp?.isProfileBuildNow ??
+        false;
+    if (isProfileBuild) {
+      return const CenteredMessage(message: debugModeOnlyMessage);
+    }
     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 1e4cda3..10b1377 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -31,6 +31,9 @@
 * Fixed a bug where the Flutter frames chart could appear frozen after
   switching away from the Performance screen and coming back.
   [#9960](https://github.com/flutter/devtools/pull/9960)
+* Added 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
 
@@ -40,15 +43,16 @@
 
 ## Memory updates
 
-* Added the ability to pin classes to the top of the Profile Memory table. [#8898](https://github.com/flutter/devtools/issues/8898)
+* Added the ability to pin classes to the top of the Profile Memory table.
+  [#8898](https://github.com/flutter/devtools/issues/8898)
 
 ## Debugger updates
 
-* Prevent values from being garbage-collected, while being evaluated.
+* Prevented values from being garbage-collected, while being evaluated.
   [#9885](https://github.com/flutter/devtools/pull/9885)
 * Update to latest version of the Dart syntax highlighting grammar
   [#9920](https://github.com/flutter/devtools/pull/9920).
-* Fix a bug in the TextMate grammar parser that could result in code after
+* Fixed a bug in the TextMate grammar parser that could result in code after
   comments being classified as comments.
   [#9921](https://github.com/flutter/devtools/pull/9921).
 * Fixed an overflow in the debugging controls when the Debugger screen is
@@ -70,7 +74,7 @@
 
 ## Logging updates
 
-* Correct time units and cumulative nature of GC events.
+* Corrected time units and cumulative nature of GC events.
   [#9890](https://github.com/flutter/devtools/pull/9890)
 * Fixed a bug where long `dart:developer` log messages lost their structured
   Details view. -
@@ -83,7 +87,7 @@
 ## Deep links tool updates
 
 * Added a "Watch tutorial" link to the status line that points to the
-  [deep links video tutorial](https://youtu.be/d7sZL6h1Elw).
+  [deep links video tutorial](https://youtu.be/d7sZL6hIElw).
   [#9925](https://github.com/flutter/devtools/pull/9925)
 
 ## VS Code sidebar updates
@@ -92,12 +96,13 @@
 
 ## DevTools extension updates
 
-* Hide the DevTools extensions menu button in single-screen embedded mode (`EmbedMode.embedOne`) on standard screens.
+* Hide the DevTools extensions menu button in single-screen embedded mode
+  (`EmbedMode.embedOne`) on standard screens.
   [#8507](https://github.com/flutter/devtools/issues/8507)
 * Added iframe sandboxing for embedded DevTools extensions to enforce origin
   isolation. [#9967](https://github.com/flutter/devtools/pull/9967)
-* Improved DevTools extension isolation by tracking the providing package name for
-  enablement, deduplication, and asset loading.
+* Improved DevTools extension isolation by tracking the providing package
+  name for enablement, deduplication, and asset loading.
   [#9981](https://github.com/flutter/devtools/pull/9981)
 
 ## Advanced developer mode updates
@@ -107,4 +112,4 @@
 ## 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.61.0).
+[DevTools git log](https://github.com/flutter/devtools/tree/v2.61.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 6f23218..8d24ef2 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,7 @@
 
 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/rebuild_stats/rebuild_stats.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';
@@ -218,7 +219,6 @@
           await tester.runAsync(() async {
             await pumpPerformanceScreen(tester, runAsync: true);
             await tester.pumpAndSettle();
-
             final chartButtonFinder = find.byType(VisibilityButton);
             expect(chartButtonFinder, findsOneWidget);
 
@@ -231,7 +231,6 @@
 
             await tester.tap(chartButtonFinder);
             await tester.pumpAndSettle();
-
             // The flutter frames chart should no longer be visible.
             expect(find.byType(FramesChartControls), findsNothing);
             expect(
@@ -395,6 +394,64 @@
         },
       );
     });
+
+    group('RebuildStatsView', () {
+      late FakeServiceConnectionManager fakeServiceConnection;
+      late RebuildCountModel model;
+      late ValueNotifier<FlutterFrame?> selectedFrame;
+
+      setUp(() {
+        fakeServiceConnection = FakeServiceConnectionManager();
+        mockConnectedApp(fakeServiceConnection.serviceManager.connectedApp!);
+        setGlobal(ServiceConnectionManager, fakeServiceConnection);
+        setGlobal(IdeTheme, IdeTheme());
+        setGlobal(NotificationService, NotificationService());
+        setGlobal(BannerMessagesController, BannerMessagesController());
+        setGlobal(PreferencesController, PreferencesController());
+        setGlobal(OfflineDataController, OfflineDataController());
+        model = RebuildCountModel();
+        selectedFrame = ValueNotifier<FlutterFrame?>(null);
+      });
+
+      testWidgets('shows message when running in profile mode', (
+        WidgetTester tester,
+      ) async {
+        mockConnectedApp(
+          fakeServiceConnection.serviceManager.connectedApp!,
+          isProfileBuild: 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 {
+        mockConnectedApp(fakeServiceConnection.serviceManager.connectedApp!);
+
+        await tester.pumpWidget(
+          wrapWithControllers(
+            RebuildStatsView(model: model, selectedFrame: selectedFrame),
+          ),
+        );
+        await tester.pump();
+
+        expect(
+          find.textContaining('Widget rebuild counts are only available'),
+          findsNothing,
+        );
+      });
+    });
   });
 }