AI assistant pane is displayed for supported screens when the AI assistant feature flag is enabled (#9591)
diff --git a/packages/devtools_app/integration_test/test/live_connection/memory_screen_helpers.dart b/packages/devtools_app/integration_test/test/live_connection/memory_screen_helpers.dart
index 87efb70..2753d97 100644
--- a/packages/devtools_app/integration_test/test/live_connection/memory_screen_helpers.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/memory_screen_helpers.dart
@@ -3,9 +3,9 @@
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/framework/scaffold/bottom_pane.dart';
import 'package:devtools_app/src/screens/memory/panes/control/widgets/primary_controls.dart';
import 'package:devtools_app/src/screens/memory/panes/diff/widgets/snapshot_list.dart';
-import 'package:devtools_app/src/shared/console/widgets/console_pane.dart';
import 'package:devtools_test/helpers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -45,7 +45,7 @@
// but not too big to make classes in snapshot hidden.
const dragDistance = -320.0;
await tester.drag(
- find.byType(ConsolePaneHeader),
+ find.byKey(BottomPane.splitterKey),
const Offset(0, dragDistance),
);
await tester.pumpAndSettle();
diff --git a/packages/devtools_app/lib/src/framework/scaffold/bottom_pane.dart b/packages/devtools_app/lib/src/framework/scaffold/bottom_pane.dart
new file mode 100644
index 0000000..bba85e2
--- /dev/null
+++ b/packages/devtools_app/lib/src/framework/scaffold/bottom_pane.dart
@@ -0,0 +1,38 @@
+// 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.
+
+import 'package:flutter/material.dart';
+
+import '../../shared/ui/tab.dart';
+
+/// A widget that displays a tabbed view at the bottom of the DevTools screen.
+///
+/// This widget is used to host views like the console and the AI Assistant.
+class BottomPane extends StatelessWidget {
+ const BottomPane({super.key, required this.screenId, required this.tabs})
+ : assert(tabs.length > 0);
+
+ static const splitterKey = Key('Bottom Pane Splitter');
+
+ final String screenId;
+ final List<TabbedPane> tabs;
+
+ @override
+ Widget build(BuildContext context) {
+ return AnalyticsTabbedView(
+ gaScreen: screenId,
+ tabs: tabs
+ .map((tabbedPane) => (tab: tabbedPane.tab, tabView: tabbedPane))
+ .toList(),
+ staticSingleTab: true,
+ );
+ }
+}
+
+/// An interface for a widget that should be displayed as a tab in the
+/// [BottomPane].
+abstract class TabbedPane implements Widget {
+ /// The tab to display for this pane.
+ DevToolsTab get tab;
+}
diff --git a/packages/devtools_app/lib/src/framework/scaffold/scaffold.dart b/packages/devtools_app/lib/src/framework/scaffold/scaffold.dart
index 823ad42..978f7b3 100644
--- a/packages/devtools_app/lib/src/framework/scaffold/scaffold.dart
+++ b/packages/devtools_app/lib/src/framework/scaffold/scaffold.dart
@@ -11,6 +11,7 @@
import '../../app.dart';
import '../../extensions/extension_settings.dart';
import '../../screens/debugger/debugger_screen.dart';
+import '../../shared/ai_assistant/widgets/ai_assistant_pane.dart';
import '../../shared/analytics/prompt.dart';
import '../../shared/config_specific/drag_and_drop/drag_and_drop.dart';
import '../../shared/config_specific/import_export/import_export.dart';
@@ -25,6 +26,7 @@
import '../../shared/title.dart';
import 'about_dialog.dart';
import 'app_bar.dart';
+import 'bottom_pane.dart';
import 'report_feedback_button.dart';
import 'settings_dialog.dart';
import 'status_line.dart';
@@ -310,10 +312,16 @@
return Provider<ImportController>.value(
value: _importController,
builder: (context, _) {
- final showConsole =
+ final isConnectedAppView =
serviceConnection.serviceManager.connectedAppInitialized &&
- !offlineDataController.showingOfflineData.value &&
- _currentScreen.showConsole(widget.embedMode);
+ !offlineDataController.showingOfflineData.value;
+ final showConsole =
+ isConnectedAppView && _currentScreen.showConsole(widget.embedMode);
+ final showAiAssistant =
+ FeatureFlags.aiAssistant.isEnabled &&
+ isConnectedAppView &&
+ _currentScreen.showAiAssistant();
+ final showBottomPane = showConsole || showAiAssistant;
final containsSingleSimpleScreen =
widget.screens.length == 1 && widget.screens.first is SimpleScreen;
final showAppBar =
@@ -345,20 +353,24 @@
body: OutlineDecoration.onlyTop(
child: Padding(
padding: widget.appPadding,
- child: showConsole
+ child: showBottomPane
? SplitPane(
axis: Axis.vertical,
- splitters: [ConsolePaneHeader()],
initialFractions: const [0.8, 0.2],
- children: [
- Padding(
- padding: const EdgeInsets.only(
- bottom: intermediateSpacing,
- ),
- child: content,
+ splitters: const [
+ DefaultSplitter(
+ key: BottomPane.splitterKey,
+ isHorizontal: true,
),
- RoundedOutlinedBorder.onlyBottom(
- child: const ConsolePane(),
+ ],
+ children: [
+ content,
+ BottomPane(
+ screenId: _currentScreen.screenId,
+ tabs: [
+ if (showConsole) const ConsolePane(),
+ if (showAiAssistant) const AiAssistantPane(),
+ ],
),
],
)
diff --git a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_screen.dart b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_screen.dart
index b8b7744..710743d 100644
--- a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_screen.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_screen.dart
@@ -26,6 +26,9 @@
bool showConsole(EmbedMode embedMode) => !embedMode.embedded;
@override
+ bool showAiAssistant() => true;
+
+ @override
String get docPageId => screenId;
@override
diff --git a/packages/devtools_app/lib/src/screens/network/network_screen.dart b/packages/devtools_app/lib/src/screens/network/network_screen.dart
index cd52632..5171c5e 100644
--- a/packages/devtools_app/lib/src/screens/network/network_screen.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart
@@ -40,6 +40,9 @@
String get docPageId => screenId;
@override
+ bool showAiAssistant() => true;
+
+ @override
Widget buildScreenBody(BuildContext context) => const NetworkScreenBody();
@override
diff --git a/packages/devtools_app/lib/src/shared/ai_assistant/widgets/ai_assistant_pane.dart b/packages/devtools_app/lib/src/shared/ai_assistant/widgets/ai_assistant_pane.dart
new file mode 100644
index 0000000..b3d2128
--- /dev/null
+++ b/packages/devtools_app/lib/src/shared/ai_assistant/widgets/ai_assistant_pane.dart
@@ -0,0 +1,29 @@
+// 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.
+
+import 'package:flutter/material.dart';
+
+import '../../../framework/scaffold/bottom_pane.dart';
+import '../../ui/tab.dart';
+
+class AiAssistantPane extends StatelessWidget implements TabbedPane {
+ const AiAssistantPane({super.key});
+
+ @override
+ DevToolsTab get tab =>
+ DevToolsTab.create(tabName: _tabName, gaPrefix: _gaPrefix);
+
+ static const _tabName = 'AI Assistant';
+
+ static const _gaPrefix = 'aiAssistant';
+
+ @override
+ Widget build(BuildContext context) {
+ return const Column(
+ children: [
+ Expanded(child: Center(child: Text('TODO: Implement AI Assistant.'))),
+ ],
+ );
+ }
+}
diff --git a/packages/devtools_app/lib/src/shared/console/widgets/console_pane.dart b/packages/devtools_app/lib/src/shared/console/widgets/console_pane.dart
index 5a40db7..4d661cc 100644
--- a/packages/devtools_app/lib/src/shared/console/widgets/console_pane.dart
+++ b/packages/devtools_app/lib/src/shared/console/widgets/console_pane.dart
@@ -6,8 +6,10 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
+import '../../../framework/scaffold/bottom_pane.dart';
import '../../globals.dart';
import '../../ui/common_widgets.dart';
+import '../../ui/tab.dart';
import '../console.dart';
import '../console_service.dart';
import 'evaluate.dart';
@@ -15,31 +17,8 @@
// TODO(devoncarew): Show some small UI indicator when we receive stdout/stderr.
-class ConsolePaneHeader extends AreaPaneHeader {
- ConsolePaneHeader({super.key})
- : super(
- title: const Text('Console'),
- roundedTopBorder: true,
- actions: [
- const ConsoleHelpLink(),
- const SizedBox(width: densePadding),
- CopyToClipboardControl(
- dataProvider: () =>
- serviceConnection.consoleService.stdio.value.join('\n'),
- buttonKey: ConsolePane.copyToClipboardButtonKey,
- ),
- const SizedBox(width: densePadding),
- DeleteControl(
- buttonKey: ConsolePane.clearStdioButtonKey,
- tooltip: 'Clear console output',
- onPressed: () => serviceConnection.consoleService.clearStdio(),
- ),
- ],
- );
-}
-
/// Display the stdout and stderr output from the process under debug.
-class ConsolePane extends StatelessWidget {
+class ConsolePane extends StatelessWidget implements TabbedPane {
const ConsolePane({super.key});
static const copyToClipboardButtonKey = Key(
@@ -47,6 +26,17 @@
);
static const clearStdioButtonKey = Key('console_clear_stdio_button');
+ static const _tabName = 'Console';
+
+ static const _gaPrefix = 'consolePane';
+
+ @override
+ DevToolsTab get tab => DevToolsTab.create(
+ tabName: _tabName,
+ gaPrefix: _gaPrefix,
+ trailing: const _ConsoleActions(),
+ );
+
ValueListenable<List<ConsoleLine>> get stdio =>
serviceConnection.consoleService.stdio;
@@ -61,10 +51,30 @@
footer = const ExpressionEvalField();
}
- return Column(
- children: [
- Expanded(
- child: Console(lines: stdio, footer: footer),
+ return Console(lines: stdio, footer: footer);
+ }
+}
+
+class _ConsoleActions extends StatelessWidget {
+ const _ConsoleActions();
+
+ @override
+ Widget build(BuildContext context) {
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ children: <Widget>[
+ const ConsoleHelpLink(),
+ const SizedBox(width: densePadding),
+ CopyToClipboardControl(
+ dataProvider: () =>
+ serviceConnection.consoleService.stdio.value.join('\n'),
+ buttonKey: ConsolePane.copyToClipboardButtonKey,
+ ),
+ const SizedBox(width: densePadding),
+ DeleteControl(
+ buttonKey: ConsolePane.clearStdioButtonKey,
+ tooltip: 'Clear console output',
+ onPressed: () => serviceConnection.consoleService.clearStdio(),
),
],
);
diff --git a/packages/devtools_app/lib/src/shared/feature_flags.dart b/packages/devtools_app/lib/src/shared/feature_flags.dart
index e15d825..88a8b24 100644
--- a/packages/devtools_app/lib/src/shared/feature_flags.dart
+++ b/packages/devtools_app/lib/src/shared/feature_flags.dart
@@ -85,6 +85,14 @@
enabled: true,
);
+ /// Flag to enable the AI Assistant.
+ ///
+ /// https://github.com/flutter/devtools/issues/9590
+ static final aiAssistant = BooleanFeatureFlag(
+ name: 'aiAssistant',
+ enabled: enableExperiments,
+ );
+
/// A set of all the boolean feature flags for debugging purposes.
///
/// When adding a new boolean flag, you are responsible for adding it to this
@@ -95,6 +103,7 @@
devToolsExtensions,
dapDebugging,
inspectorV2,
+ aiAssistant,
};
/// A set of all the Flutter channel feature flags for debugging purposes.
diff --git a/packages/devtools_app/lib/src/shared/framework/screen.dart b/packages/devtools_app/lib/src/shared/framework/screen.dart
index c3b4931..0cdc840 100644
--- a/packages/devtools_app/lib/src/shared/framework/screen.dart
+++ b/packages/devtools_app/lib/src/shared/framework/screen.dart
@@ -290,6 +290,9 @@
/// Whether to show the console for this screen.
bool showConsole(EmbedMode embedMode) => false;
+ /// Whether to show the AI Assistant for this screen.
+ bool showAiAssistant() => false;
+
/// Which keyboard shortcuts should be enabled for this screen.
ShortcutsConfiguration buildKeyboardShortcuts(BuildContext context) =>
ShortcutsConfiguration.empty();
diff --git a/packages/devtools_app/lib/src/shared/ui/tab.dart b/packages/devtools_app/lib/src/shared/ui/tab.dart
index ebd6e2a..6961b55 100644
--- a/packages/devtools_app/lib/src/shared/ui/tab.dart
+++ b/packages/devtools_app/lib/src/shared/ui/tab.dart
@@ -81,6 +81,7 @@
this.onTabChanged,
this.initialSelectedIndex,
this.analyticsSessionIdentifier,
+ this.staticSingleTab = false,
}) : trailingWidgets = List.generate(
tabs.length,
(index) => tabs[index].tab.trailing ?? const SizedBox(),
@@ -106,6 +107,10 @@
/// events.
final String? analyticsSessionIdentifier;
+ /// When there is only a single tab, whether to display that tab as a static
+ /// title instead of in a [TabBar].
+ final bool staticSingleTab;
+
/// Whether to send analytics events to GA.
///
/// Only set this to false if [AnalyticsTabbedView] is being used for
@@ -202,13 +207,10 @@
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Expanded(
- child: TabBar(
- labelColor: Theme.of(context).colorScheme.onSurface,
- controller: _tabController,
- tabs: widget.tabs.map((t) => t.tab).toList(),
- isScrollable: true,
- ),
+ _AnalyticsTabBar(
+ tabs: widget.tabs.map((t) => t.tab).toList(),
+ tabController: _tabController,
+ staticSingleTab: widget.staticSingleTab,
),
widget.trailingWidgets[_currentTabControllerIndex],
],
@@ -233,3 +235,34 @@
);
}
}
+
+/// A [TabBar] used by [AnalyticsTabbedView].
+///
+/// When there is only a single tab and [staticSingleTab] is true, this tab bar
+/// will be displayed as a static title.
+class _AnalyticsTabBar extends StatelessWidget {
+ const _AnalyticsTabBar({
+ required this.tabs,
+ required this.tabController,
+ required this.staticSingleTab,
+ });
+
+ static const _tabPadding = 14.0;
+
+ final List<DevToolsTab> tabs;
+ final TabController? tabController;
+ final bool staticSingleTab;
+
+ @override
+ Widget build(BuildContext context) => (staticSingleTab && tabs.length == 1)
+ ? Padding(
+ padding: const EdgeInsets.symmetric(horizontal: _tabPadding),
+ child: tabs.first,
+ )
+ : TabBar(
+ labelColor: Theme.of(context).colorScheme.onSurface,
+ controller: tabController,
+ tabs: tabs,
+ isScrollable: true,
+ );
+}
diff --git a/packages/devtools_app/test/framework/scaffold/scaffold_ai_assistant_test.dart b/packages/devtools_app/test/framework/scaffold/scaffold_ai_assistant_test.dart
new file mode 100644
index 0000000..8b3d04f
--- /dev/null
+++ b/packages/devtools_app/test/framework/scaffold/scaffold_ai_assistant_test.dart
@@ -0,0 +1,159 @@
+// 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.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/framework/scaffold/scaffold.dart';
+import 'package:devtools_app/src/shared/ai_assistant/widgets/ai_assistant_pane.dart';
+import 'package:devtools_app/src/shared/feature_flags.dart';
+import 'package:devtools_app/src/shared/framework/framework_controller.dart';
+import 'package:devtools_app/src/shared/managers/survey.dart';
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+
+void main() {
+ late MockServiceConnectionManager mockServiceConnection;
+ late MockServiceManager mockServiceManager;
+
+ setUp(() {
+ mockServiceConnection = createMockServiceConnectionWithDefaults();
+ mockServiceManager =
+ mockServiceConnection.serviceManager as MockServiceManager;
+ when(
+ mockServiceManager.connectedState,
+ ).thenReturn(ValueNotifier<ConnectedState>(const ConnectedState(false)));
+ final mockErrorBadgeManager = MockErrorBadgeManager();
+ when(
+ mockServiceConnection.errorBadgeManager,
+ ).thenReturn(mockErrorBadgeManager);
+ when(
+ mockErrorBadgeManager.errorCountNotifier(any),
+ ).thenReturn(ValueNotifier<int>(0));
+
+ setGlobal(ServiceConnectionManager, mockServiceConnection);
+ setGlobal(FrameworkController, FrameworkController());
+ setGlobal(SurveyService, SurveyService());
+ setGlobal(IdeTheme, IdeTheme());
+ setGlobal(NotificationService, NotificationService());
+ setGlobal(BannerMessagesController, BannerMessagesController());
+ });
+
+ Future<void> pumpScaffold(
+ WidgetTester tester, {
+ required Screen screen,
+ bool withConnectedApp = true,
+ bool withOfflineData = false,
+ }) async {
+ if (withOfflineData) {
+ final offlineController = MockOfflineDataController();
+ offlineController.showingOfflineData.value = true;
+ setGlobal(OfflineDataController, offlineController);
+ }
+
+ MockConnectedApp? connectedApp;
+ if (withConnectedApp) {
+ connectedApp = MockConnectedApp();
+ mockConnectedApp(connectedApp);
+ }
+ when(
+ mockServiceManager.connectedAppInitialized,
+ ).thenReturn(withConnectedApp);
+ when(mockServiceManager.connectedApp).thenReturn(connectedApp);
+
+ await tester.pumpWidget(
+ wrapWithControllers(
+ DevToolsScaffold(screens: [screen]),
+ analytics: AnalyticsController(
+ enabled: false,
+ shouldShowConsentMessage: false,
+ consentMessage: 'fake message',
+ ),
+ ),
+ );
+ }
+
+ group('AI Assistant pane', () {
+ testWidgets('is visible for supported screens', (
+ WidgetTester tester,
+ ) async {
+ FeatureFlags.aiAssistant.setEnabledForTests(true);
+
+ await pumpScaffold(tester, screen: const _TestScreenWithAi());
+
+ expect(find.byType(AiAssistantPane), findsOneWidget);
+ });
+
+ testWidgets('is not visible for unsupported screens', (
+ WidgetTester tester,
+ ) async {
+ FeatureFlags.aiAssistant.setEnabledForTests(true);
+
+ await pumpScaffold(tester, screen: const _TestScreenWithoutAi());
+
+ expect(find.byType(AiAssistantPane), findsNothing);
+ });
+
+ testWidgets('is not visible when app is not connected', (
+ WidgetTester tester,
+ ) async {
+ FeatureFlags.aiAssistant.setEnabledForTests(true);
+
+ await pumpScaffold(
+ tester,
+ screen: const _TestScreenWithAi(),
+ withConnectedApp: false,
+ );
+
+ expect(find.byType(AiAssistantPane), findsNothing);
+ });
+
+ testWidgets('is not visible when feature flag is disabled', (
+ WidgetTester tester,
+ ) async {
+ FeatureFlags.aiAssistant.setEnabledForTests(false);
+
+ await pumpScaffold(tester, screen: const _TestScreenWithAi());
+
+ expect(find.byType(AiAssistantPane), findsNothing);
+ });
+
+ testWidgets('is not visible when in offline mode', (
+ WidgetTester tester,
+ ) async {
+ FeatureFlags.aiAssistant.setEnabledForTests(true);
+
+ await pumpScaffold(
+ tester,
+ screen: const _TestScreenWithAi(),
+ withOfflineData: true,
+ );
+
+ expect(find.byType(AiAssistantPane), findsNothing);
+ });
+ });
+}
+
+class _TestScreenWithAi extends Screen {
+ const _TestScreenWithAi()
+ : super('test_screen_with_ai', showFloatingDebuggerControls: false);
+
+ @override
+ bool showAiAssistant() => true;
+
+ @override
+ Widget buildScreenBody(BuildContext context) => const SizedBox();
+}
+
+class _TestScreenWithoutAi extends Screen {
+ const _TestScreenWithoutAi()
+ : super('test_screen_without_ai', showFloatingDebuggerControls: false);
+
+ @override
+ Widget buildScreenBody(BuildContext context) => const SizedBox();
+}
diff --git a/packages/devtools_app/test/screens/debugger/debugger_console_test.dart b/packages/devtools_app/test/screens/debugger/debugger_console_test.dart
index 3552e90..392bc0d 100644
--- a/packages/devtools_app/test/screens/debugger/debugger_console_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_console_test.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/framework/scaffold/bottom_pane.dart';
import 'package:devtools_app/src/shared/console/widgets/console_pane.dart';
import 'package:devtools_app_shared/ui.dart';
import 'package:devtools_app_shared/utils.dart';
@@ -51,8 +52,12 @@
wrapWithControllers(
Row(
children: [
- Flexible(child: ConsolePaneHeader()),
- const Expanded(child: ConsolePane()),
+ Expanded(
+ child: BottomPane(
+ screenId: 'debugger',
+ tabs: const [ConsolePane()],
+ ),
+ ),
],
),
debugger: controller,
diff --git a/packages/devtools_app/test/screens/debugger/debugger_screen_test.dart b/packages/devtools_app/test/screens/debugger/debugger_screen_test.dart
index 63bc094..daa2f64 100644
--- a/packages/devtools_app/test/screens/debugger/debugger_screen_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_screen_test.dart
@@ -4,6 +4,7 @@
import 'package:collection/collection.dart';
import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/framework/scaffold/bottom_pane.dart';
import 'package:devtools_app/src/screens/debugger/debugger_model.dart';
import 'package:devtools_app/src/shared/console/widgets/console_pane.dart';
import 'package:devtools_app_shared/ui.dart';
@@ -85,8 +86,12 @@
wrapWithControllers(
Row(
children: [
- Flexible(child: ConsolePaneHeader()),
- const Expanded(child: ConsolePane()),
+ Expanded(
+ child: BottomPane(
+ screenId: 'debugger',
+ tabs: const [ConsolePane()],
+ ),
+ ),
],
),
debugger: controller,
diff --git a/packages/devtools_app/test/shared/ansi_output_test.dart b/packages/devtools_app/test/shared/ansi_output_test.dart
index e0de53d..639593c 100644
--- a/packages/devtools_app/test/shared/ansi_output_test.dart
+++ b/packages/devtools_app/test/shared/ansi_output_test.dart
@@ -252,12 +252,7 @@
) async {
await tester.pumpWidget(
wrapWithControllers(
- Row(
- children: [
- Flexible(child: ConsolePaneHeader()),
- const Expanded(child: ConsolePane()),
- ],
- ),
+ const Row(children: [Expanded(child: ConsolePane())]),
debugger: controller,
),
);
diff --git a/packages/devtools_app/test/shared/primitives/feature_flags_test.dart b/packages/devtools_app/test/shared/primitives/feature_flags_test.dart
index 1ac9a1a..826708d 100644
--- a/packages/devtools_app/test/shared/primitives/feature_flags_test.dart
+++ b/packages/devtools_app/test/shared/primitives/feature_flags_test.dart
@@ -21,6 +21,7 @@
expect(FeatureFlags.devToolsExtensions.isEnabled, isExternalBuild);
expect(FeatureFlags.dapDebugging.isEnabled, false);
expect(FeatureFlags.inspectorV2.isEnabled, true);
+ expect(FeatureFlags.aiAssistant.isEnabled, false);
});
group('FlutterChannelFeatureFlag', () {
diff --git a/packages/devtools_app_shared/lib/src/ui/split_pane.dart b/packages/devtools_app_shared/lib/src/ui/split_pane.dart
index b870cec..59e5ef5 100644
--- a/packages/devtools_app_shared/lib/src/ui/split_pane.dart
+++ b/packages/devtools_app_shared/lib/src/ui/split_pane.dart
@@ -295,7 +295,8 @@
}
}
-final class DefaultSplitter extends StatelessWidget {
+final class DefaultSplitter extends StatelessWidget
+ implements PreferredSizeWidget {
const DefaultSplitter({super.key, required this.isHorizontal});
static const iconSize = 24.0;
@@ -304,6 +305,9 @@
final bool isHorizontal;
@override
+ Size get preferredSize => const Size(splitterWidth, iconSize);
+
+ @override
Widget build(BuildContext context) {
return Transform.rotate(
angle: isHorizontal ? degToRad(90.0) : degToRad(0.0),