Fix DropdownMenu keyboard navigation (#147294)

Fix https://github.com/flutter/flutter/issues/147253
Fix https://github.com/flutter/flutter/issues/147516

Resubmitted https://github.com/flutter/flutter/pull/147285 because I accidentally added reviewer as contributor in a commit by using Github "add suggestion to batch". Which causing CLA check to fail and I cannot revert it.

I use exact match of label instead of contains to minimise possible change in actual user search.
I added the new test after the original keyboard navigation test to avoid merge conflict with another incoming PR.
Let me know if I need to make any changes.

Sorry for the inconvenience, I am new to the process.
This commit is contained in:
PurplePolyhedron 2024-05-11 07:55:49 +10:00 committed by GitHub
parent da9712f76f
commit fdfc408fd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 113 additions and 0 deletions

View File

@ -550,6 +550,9 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
if (searchText.isEmpty) {
return null;
}
if (currentHighlight != null && entries[currentHighlight!].label.toLowerCase().contains(searchText)) {
return currentHighlight;
}
final int index = entries.indexWhere((DropdownMenuEntry<T> entry) => entry.label.toLowerCase().contains(searchText));
return index != -1 ? index : null;

View File

@ -764,6 +764,116 @@ void main() {
expect(item5material.color, Colors.transparent); // the previous item should not be highlighted.
}, variant: TargetPlatformVariant.desktop());
// Regression test for https://github.com/flutter/flutter/issues/147253.
testWidgets('Down key and up key can navigate on desktop platforms '
'when a label text contains another label text', (WidgetTester tester) async {
final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp(
theme: themeData,
home: const Scaffold(
body: DropdownMenu<int>(
dropdownMenuEntries: <DropdownMenuEntry<int>>[
DropdownMenuEntry<int>(
value: 0,
label: 'ABC'
),
DropdownMenuEntry<int>(
value: 1,
label: 'AB'
),
DropdownMenuEntry<int>(
value: 2,
label: 'ABCD'
),
],
),
),
));
await tester.tap(find.byType(DropdownMenu<int>));
await tester.pump();
final Finder button0Material = find.descendant(
of: find.widgetWithText(MenuItemButton, 'ABC').last,
matching: find.byType(Material),
);
final Finder button1Material = find.descendant(
of: find.widgetWithText(MenuItemButton, 'AB').last,
matching: find.byType(Material),
);
final Finder button2Material = find.descendant(
of: find.widgetWithText(MenuItemButton, 'ABCD').last,
matching: find.byType(Material),
);
// Press down key three times, the highlight should move to the next item each time.
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pumpAndSettle();
Material item0Material = tester.widget<Material>(button0Material);
expect(item0Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pumpAndSettle();
Material item1Material = tester.widget<Material>(button1Material);
expect(item1Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pumpAndSettle();
final Material item2Material = tester.widget<Material>(button2Material);
expect(item2Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
// Press up key two times, the highlight should up each time.
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pumpAndSettle();
item1Material = tester.widget<Material>(button1Material);
expect(item1Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
await tester.pumpAndSettle();
item0Material = tester.widget<Material>(button0Material);
expect(item0Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
}, variant: TargetPlatformVariant.desktop());
// Regression test for https://github.com/flutter/flutter/issues/147253.
testWidgets('Default search prioritises the current highlight on desktop platforms',
(WidgetTester tester) async {
final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp(
theme: themeData,
home: Scaffold(
body: DropdownMenu<TestMenu>(
dropdownMenuEntries: menuChildren,
),
),
));
const String itemLabel = 'Item 2';
// Open the menu
await tester.tap(find.byType(DropdownMenu<TestMenu>));
await tester.pumpAndSettle();
// Highlight the third item by exact search.
await tester.enterText(find.byType(TextField).first, itemLabel);
await tester.pumpAndSettle();
Finder button2Material = find.descendant(
of: find.widgetWithText(MenuItemButton, itemLabel).last,
matching: find.byType(Material),
);
Material item2material = tester.widget<Material>(button2Material);
expect(item2material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
// Search something that matches multiple items.
await tester.enterText(find.byType(TextField).first, 'Item');
await tester.pumpAndSettle();
// The third item should still be highlighted.
button2Material = find.descendant(
of: find.widgetWithText(MenuItemButton, itemLabel).last,
matching: find.byType(Material),
);
item2material = tester.widget<Material>(button2Material);
expect(item2material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
}, variant: TargetPlatformVariant.desktop());
testWidgets('The text input should match the label of the menu item '
'while pressing down key on desktop platforms', (WidgetTester tester) async {
final ThemeData themeData = ThemeData();