Skip to content

Commit 1a35297

Browse files
committed
Show debug-mode message for widget rebuilds in profile mode
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
1 parent 9e3f94d commit 1a35297

2 files changed

Lines changed: 106 additions & 0 deletions

File tree

packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ class _RebuildStatsViewState extends State<RebuildStatsView>
9292

9393
@override
9494
Widget build(BuildContext context) {
95+
final isProfileBuild =
96+
serviceConnection.serviceManager.connectedApp?.isProfileBuildNow ??
97+
false;
98+
if (isProfileBuild) {
99+
return const Center(
100+
child: Text(
101+
'Rebuild information is not available for this frame.\n'
102+
'Widget rebuild counts are only available when running '
103+
'an app in debug-mode.',
104+
textAlign: TextAlign.center,
105+
),
106+
);
107+
}
95108
return Column(
96109
crossAxisAlignment: CrossAxisAlignment.start,
97110
children: [
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2026 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
4+
5+
import 'dart:async';
6+
7+
import 'package:devtools_app/devtools_app.dart';
8+
import 'package:devtools_app/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart';
9+
import 'package:devtools_app/src/screens/performance/panes/rebuild_stats/rebuild_stats_model.dart';
10+
import 'package:devtools_app/src/screens/performance/panes/flutter_frames/flutter_frame_model.dart';
11+
import 'package:devtools_app_shared/service.dart';
12+
import 'package:devtools_app/devtools_app.dart';
13+
import 'package:devtools_app_shared/ui.dart';
14+
import 'package:devtools_app_shared/utils.dart';
15+
import 'package:devtools_test/devtools_test.dart';
16+
import 'package:devtools_test/helpers.dart';
17+
import 'package:flutter/foundation.dart';
18+
import 'package:flutter/material.dart';
19+
import 'package:flutter_test/flutter_test.dart';
20+
import 'package:mockito/mockito.dart';
21+
22+
void main() {
23+
group('RebuildStatsView', () {
24+
late FakeServiceConnectionManager fakeServiceConnection;
25+
late RebuildCountModel model;
26+
late ValueNotifier<FlutterFrame?> selectedFrame;
27+
28+
setUp(() {
29+
fakeServiceConnection = FakeServiceConnectionManager();
30+
final app = fakeServiceConnection.serviceManager.connectedApp!;
31+
when(app.initialized).thenReturn(Completer()..complete(true));
32+
when(app.isDartWebAppNow).thenReturn(false);
33+
when(app.isFlutterAppNow).thenReturn(true);
34+
when(app.isDartCliAppNow).thenReturn(false);
35+
when(app.isDartWebApp).thenAnswer((_) async => false);
36+
when(app.isProfileBuild).thenAnswer((_) async => false);
37+
setGlobal(ServiceConnectionManager, fakeServiceConnection);
38+
setGlobal(IdeTheme, IdeTheme());
39+
setGlobal(NotificationService, NotificationService());
40+
setGlobal(BannerMessagesController, BannerMessagesController());
41+
setGlobal(PreferencesController, PreferencesController());
42+
setGlobal(OfflineDataController, OfflineDataController());
43+
model = RebuildCountModel();
44+
selectedFrame = ValueNotifier<FlutterFrame?>(null);
45+
});
46+
47+
testWidgets(
48+
'shows message when running in profile mode',
49+
(WidgetTester tester) async {
50+
final app = fakeServiceConnection.serviceManager.connectedApp!;
51+
when(app.isProfileBuildNow).thenReturn(true);
52+
53+
await tester.pumpWidget(
54+
wrapWithControllers(
55+
RebuildStatsView(
56+
model: model,
57+
selectedFrame: selectedFrame,
58+
),
59+
),
60+
);
61+
await tester.pump();
62+
63+
expect(
64+
find.textContaining('Widget rebuild counts are only available'),
65+
findsOneWidget,
66+
);
67+
},
68+
);
69+
70+
testWidgets(
71+
'shows normal UI when running in debug mode',
72+
(WidgetTester tester) async {
73+
final app = fakeServiceConnection.serviceManager.connectedApp!;
74+
when(app.isProfileBuildNow).thenReturn(false);
75+
76+
await tester.pumpWidget(
77+
wrapWithControllers(
78+
RebuildStatsView(
79+
model: model,
80+
selectedFrame: selectedFrame,
81+
),
82+
),
83+
);
84+
await tester.pump();
85+
86+
expect(
87+
find.textContaining('Widget rebuild counts are only available'),
88+
findsNothing,
89+
);
90+
},
91+
);
92+
});
93+
}

0 commit comments

Comments
 (0)