Skip to content

Commit d8a327c

Browse files
blerouxIvoneDjaja
authored andcommitted
Fix DropdownMenu escape key does not close the menu (flutter#178002)
## Description This PR fixes escape key not closing the menu when `DropdownMenu.requestFocusOnTap` is false. ## Related Issue Fixes [DropdownMenu menu panel does not close when pressing ESC and requestFocusOnTap is false](flutter#177993) Part of flutter#123797 ## Tests - Adds 3 tests. - Updates 2 non-related tests where I spotted some nits.
1 parent 348d440 commit d8a327c

File tree

2 files changed

+99
-8
lines changed

2 files changed

+99
-8
lines changed

packages/flutter/lib/src/material/dropdown_menu.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,7 @@ class _DropdownMenuState<T extends Object> extends State<DropdownMenu<T>> {
13261326
_ArrowUpIntent: CallbackAction<_ArrowUpIntent>(onInvoke: handleUpKeyInvoke),
13271327
_ArrowDownIntent: CallbackAction<_ArrowDownIntent>(onInvoke: handleDownKeyInvoke),
13281328
_EnterIntent: CallbackAction<_EnterIntent>(onInvoke: (_) => _handleEditingComplete()),
1329+
DismissIntent: DismissMenuAction(controller: _controller),
13291330
},
13301331
child: Stack(
13311332
children: <Widget>[
@@ -1335,6 +1336,7 @@ class _DropdownMenuState<T extends Object> extends State<DropdownMenu<T>> {
13351336
SingleActivator(LogicalKeyboardKey.arrowUp): _ArrowUpIntent(),
13361337
SingleActivator(LogicalKeyboardKey.arrowDown): _ArrowDownIntent(),
13371338
SingleActivator(LogicalKeyboardKey.enter): _EnterIntent(),
1339+
SingleActivator(LogicalKeyboardKey.escape): DismissIntent(),
13381340
},
13391341
child: Focus(
13401342
focusNode: _internalFocudeNode,

packages/flutter/test/material/dropdown_menu_test.dart

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2050,15 +2050,13 @@ void main() {
20502050
expect(isItemHighlighted(tester, themeData, 'Menu 1'), true);
20512051

20522052
// Press up to the upper item (Item 0).
2053-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
2054-
await simulateKeyUpEvent(LogicalKeyboardKey.arrowUp);
2053+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
20552054
await tester.pumpAndSettle();
20562055
expect(find.widgetWithText(TextField, 'Item 0'), findsOneWidget);
20572056
expect(isItemHighlighted(tester, themeData, 'Item 0'), true);
20582057

20592058
// Continue to move up to the last item (Item 5).
2060-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
2061-
await simulateKeyUpEvent(LogicalKeyboardKey.arrowUp);
2059+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
20622060
await tester.pumpAndSettle();
20632061
expect(find.widgetWithText(TextField, 'Item 5'), findsOneWidget);
20642062
expect(isItemHighlighted(tester, themeData, 'Item 5'), true);
@@ -2273,9 +2271,9 @@ void main() {
22732271
final ThemeData themeData = ThemeData();
22742272
final List<DropdownMenuEntry<TestMenu>> menuWithDisabledItems = <DropdownMenuEntry<TestMenu>>[
22752273
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 0'),
2276-
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 1', enabled: false),
2277-
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 2'),
2278-
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 3'),
2274+
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu1, label: 'Item 1', enabled: false),
2275+
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu2, label: 'Item 2'),
2276+
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu3, label: 'Item 3'),
22792277
];
22802278
final TextEditingController controller = TextEditingController();
22812279
addTearDown(controller.dispose);
@@ -2311,7 +2309,7 @@ void main() {
23112309
int expectedCount = 1;
23122310

23132311
// Test onSelected on key press
2314-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
2312+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
23152313
await tester.pumpAndSettle();
23162314

23172315
// On mobile platforms, the TextField cannot gain focus by default; the focus is
@@ -4063,6 +4061,97 @@ void main() {
40634061
variant: TargetPlatformVariant.all(),
40644062
);
40654063

4064+
// Regression test for https://github.com/flutter/flutter/issues/177993.
4065+
testWidgets('Pressing ESC key closes the menu when requestFocusOnTap is false', (
4066+
WidgetTester tester,
4067+
) async {
4068+
await tester.pumpWidget(
4069+
MaterialApp(
4070+
home: Material(
4071+
child: Center(
4072+
child: DropdownMenu<TestMenu>(
4073+
dropdownMenuEntries: menuChildren,
4074+
requestFocusOnTap: false,
4075+
),
4076+
),
4077+
),
4078+
),
4079+
);
4080+
4081+
// Move focus to the TextField and open the menu.
4082+
await tester.tap(find.byType(TextField));
4083+
await tester.pump();
4084+
expect(findMenuPanel(), findsOne);
4085+
4086+
// Press ESC to close the menu.
4087+
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
4088+
await tester.pump();
4089+
expect(findMenuPanel(), findsNothing);
4090+
});
4091+
4092+
testWidgets('Pressing ESC key closes the menu when requestFocusOnTap is true', (
4093+
WidgetTester tester,
4094+
) async {
4095+
await tester.pumpWidget(
4096+
MaterialApp(
4097+
home: Material(
4098+
child: Center(
4099+
child: DropdownMenu<TestMenu>(
4100+
dropdownMenuEntries: menuChildren,
4101+
requestFocusOnTap: true,
4102+
),
4103+
),
4104+
),
4105+
),
4106+
);
4107+
4108+
// Move focus to the TextField and open the menu.
4109+
await tester.tap(find.byType(TextField));
4110+
await tester.pump();
4111+
expect(findMenuPanel(), findsOne);
4112+
4113+
// Press ESC to close the menu.
4114+
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
4115+
await tester.pump();
4116+
expect(findMenuPanel(), findsNothing);
4117+
});
4118+
4119+
testWidgets(
4120+
'Pressing ESC key after changing the selected item closes the menu',
4121+
(WidgetTester tester) async {
4122+
final ThemeData themeData = ThemeData();
4123+
await tester.pumpWidget(
4124+
MaterialApp(
4125+
theme: themeData,
4126+
home: Material(
4127+
child: Center(
4128+
child: DropdownMenu<TestMenu>(
4129+
dropdownMenuEntries: menuChildren,
4130+
initialSelection: menuChildren[2].value,
4131+
),
4132+
),
4133+
),
4134+
),
4135+
);
4136+
4137+
// Move focus to the TextField and open the menu.
4138+
await tester.tap(find.byType(TextField));
4139+
await tester.pump();
4140+
expect(findMenuPanel(), findsOne);
4141+
4142+
// Move the selection.
4143+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
4144+
await tester.pump();
4145+
expect(isItemHighlighted(tester, themeData, menuChildren[3].label), isTrue);
4146+
4147+
// Press ESC to close the menu.
4148+
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
4149+
await tester.pump();
4150+
expect(findMenuPanel(), findsNothing);
4151+
},
4152+
variant: TargetPlatformVariant.all(),
4153+
);
4154+
40664155
testWidgets('DropdownMenu passes maxLines to TextField', (WidgetTester tester) async {
40674156
await tester.pumpWidget(
40684157
MaterialApp(

0 commit comments

Comments
 (0)