From fdfc408fd78c7ed8deddf7b5a546d92df6db1a9b Mon Sep 17 00:00:00 2001 From: PurplePolyhedron <120297255+PurplePolyhedron@users.noreply.github.com> Date: Sat, 11 May 2024 07:55:49 +1000 Subject: [PATCH] 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. --- .../lib/src/material/dropdown_menu.dart | 3 + .../test/material/dropdown_menu_test.dart | 110 ++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index 0461310da9b..76799e474f1 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -550,6 +550,9 @@ class _DropdownMenuState extends State> { if (searchText.isEmpty) { return null; } + if (currentHighlight != null && entries[currentHighlight!].label.toLowerCase().contains(searchText)) { + return currentHighlight; + } final int index = entries.indexWhere((DropdownMenuEntry entry) => entry.label.toLowerCase().contains(searchText)); return index != -1 ? index : null; diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index 48dfaea83b3..c2cb13c50f8 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -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( + dropdownMenuEntries: >[ + DropdownMenuEntry( + value: 0, + label: 'ABC' + ), + DropdownMenuEntry( + value: 1, + label: 'AB' + ), + DropdownMenuEntry( + value: 2, + label: 'ABCD' + ), + ], + ), + ), + )); + + await tester.tap(find.byType(DropdownMenu)); + 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(button0Material); + expect(item0Material.color, themeData.colorScheme.onSurface.withOpacity(0.12)); + + await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); + await tester.pumpAndSettle(); + Material item1Material = tester.widget(button1Material); + expect(item1Material.color, themeData.colorScheme.onSurface.withOpacity(0.12)); + + await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); + await tester.pumpAndSettle(); + final Material item2Material = tester.widget(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(button1Material); + expect(item1Material.color, themeData.colorScheme.onSurface.withOpacity(0.12)); + + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); + await tester.pumpAndSettle(); + item0Material = tester.widget(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( + dropdownMenuEntries: menuChildren, + ), + ), + )); + + const String itemLabel = 'Item 2'; + // Open the menu + await tester.tap(find.byType(DropdownMenu)); + 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(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(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();