From d2c8552944b80abd0778e39ee0710e14bca5b015 Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Tue, 26 Mar 2024 22:15:48 +0200 Subject: [PATCH] Fix disabled `DropdownMenu` doesn't defer the mouse cursor (#145686) fixes [DropdownMenu cursor in disabled state](https://github.com/flutter/flutter/issues/144611) This was added in https://github.com/flutter/flutter/pull/121353 ### Code sample
expand to view the code sample ```dart import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Spacer(), const Text('enabled: true,\nrequestFocusOnTap: true,'), const SizedBox(height: 16), DropdownMenu( enabled: true, initialSelection: 'First', requestFocusOnTap: true, width: 200, dropdownMenuEntries: ['First', 'Second', 'Third'] .map((e) => DropdownMenuEntry(value: e, label: e)) .toList(), ), const Text('Expected: text cursor'), const Spacer(), const Text('enabled: true,\nrequestFocusOnTap: false,'), const SizedBox(height: 16), DropdownMenu( enabled: true, initialSelection: 'First', requestFocusOnTap: false, width: 200, dropdownMenuEntries: ['First', 'Second', 'Third'] .map((e) => DropdownMenuEntry(value: e, label: e)) .toList(), // label: const Text('requestFocusOnTap: false'), ), const Text('Expected: clickable cursor'), const Spacer(), const Text('enabled: false,\nrequestFocusOnTap: true,'), const SizedBox(height: 16), DropdownMenu( enabled: false, initialSelection: 'First', requestFocusOnTap: true, width: 200, dropdownMenuEntries: ['First', 'Second', 'Third'] .map((e) => DropdownMenuEntry(value: e, label: e)) .toList(), ), const Text('Expected: deferred cursor'), const Spacer(), const Text('enabled: false,\nrequestFocusOnTap: false,'), const SizedBox(height: 16), DropdownMenu( enabled: false, initialSelection: 'First', requestFocusOnTap: false, width: 200, dropdownMenuEntries: ['First', 'Second', 'Third'] .map((e) => DropdownMenuEntry(value: e, label: e)) .toList(), ), const Text('Expected: deferred cursor'), const Spacer(), ], ), ), ), ); } } ```
### Preview ![Screenshot 2024-03-25 at 14 52 31](https://github.com/flutter/flutter/assets/48603081/cf4361a5-d3bb-4635-9825-5eefa4efe6cc) --- .../lib/src/material/dropdown_menu.dart | 5 +- .../test/material/dropdown_menu_test.dart | 62 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index d1cd8cfd9bd..6ac09b52ee4 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -699,7 +699,10 @@ class _DropdownMenuState extends State> { ?? theme.inputDecorationTheme ?? defaults.inputDecorationTheme!; - final MouseCursor effectiveMouseCursor = canRequestFocus() ? SystemMouseCursors.text : SystemMouseCursors.click; + final MouseCursor? effectiveMouseCursor = switch (widget.enabled) { + true => canRequestFocus() ? SystemMouseCursors.text : SystemMouseCursors.click, + false => null, + }; Widget menuAnchor = MenuAnchor( style: effectiveMenuStyle, diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index 1079292afbf..eff976fd1a2 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -1342,6 +1342,68 @@ void main() { expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click); }); + testWidgets('If enabled is false, the mouse cursor should be deferred when hovered', (WidgetTester tester) async { + Widget buildDropdownMenu({ bool enabled = true, bool? requestFocusOnTap }) { + return MaterialApp( + home: Scaffold( + body: Column( + children: [ + DropdownMenu( + enabled: enabled, + requestFocusOnTap: requestFocusOnTap, + dropdownMenuEntries: menuChildren, + ), + ], + ), + ), + ); + } + + // Check mouse cursor dropdown menu is disabled and requestFocusOnTap is true. + await tester.pumpWidget(buildDropdownMenu(enabled: false, requestFocusOnTap: true)); + await tester.pumpAndSettle(); + + Finder textFieldFinder = find.byType(TextField); + TextField textField = tester.widget(textFieldFinder); + expect(textField.canRequestFocus, true); + + final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1); + await gesture.moveTo(tester.getCenter(textFieldFinder)); + expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic); + + // Remove the pointer. + await gesture.removePointer(); + + // Check mouse cursor dropdown menu is disabled and requestFocusOnTap is false. + await tester.pumpWidget(buildDropdownMenu(enabled: false, requestFocusOnTap: false)); + await tester.pumpAndSettle(); + + textFieldFinder = find.byType(TextField); + textField = tester.widget(textFieldFinder); + expect(textField.canRequestFocus, false); + + // Add a new pointer. + await gesture.addPointer(); + await gesture.moveTo(tester.getCenter(textFieldFinder)); + expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic); + + // Remove the pointer. + await gesture.removePointer(); + + // Check enabled dropdown menu updates the mouse cursor when hovered. + await tester.pumpWidget(buildDropdownMenu(requestFocusOnTap: true)); + await tester.pumpAndSettle(); + + textFieldFinder = find.byType(TextField); + textField = tester.widget(textFieldFinder); + expect(textField.canRequestFocus, true); + + // Add a new pointer. + await gesture.addPointer(); + await gesture.moveTo(tester.getCenter(textFieldFinder)); + expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text); + }); + testWidgets('The menu has the same width as the input field in ListView', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/123631 await tester.pumpWidget(MaterialApp(