From b5847d364a26d727af58ab885a6123e0e5304b2b Mon Sep 17 00:00:00 2001 From: PurplePolyhedron <120297255+PurplePolyhedron@users.noreply.github.com> Date: Thu, 15 Aug 2024 02:06:14 +0800 Subject: [PATCH] Delay `DropdownMenu` filtering until text input (#152368) fixes https://github.com/flutter/flutter/issues/152055 Disabling filtering in `DropdownMenu` at start and after a selection has been made, and re-enable it (if `widget.enableFilter` is true) after text input. This way it doesn't hide all other options when there is an existing selection. Note: currently this may crush due to issue https://github.com/flutter/flutter/issues/151878 . Which is not directly related to this PR --- .../lib/src/material/dropdown_menu.dart | 11 ++++- .../test/material/dropdown_menu_test.dart | 43 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index 74a9be79dd..856ce5824c 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -488,7 +488,7 @@ class _DropdownMenuState extends State> { final GlobalKey _leadingKey = GlobalKey(); late List buttonItemKeys; final MenuController _controller = MenuController(); - late bool _enableFilter; + bool _enableFilter = false; late List> filteredEntries; List? _initialMenu; int? currentHighlight; @@ -504,7 +504,6 @@ class _DropdownMenuState extends State> { } else { _localTextEditingController = TextEditingController(); } - _enableFilter = widget.enableFilter; filteredEntries = widget.dropdownMenuEntries; buttonItemKeys = List.generate(filteredEntries.length, (int index) => GlobalKey()); _menuHasEnabledItem = filteredEntries.any((DropdownMenuEntry entry) => entry.enabled); @@ -537,6 +536,11 @@ class _DropdownMenuState extends State> { } _localTextEditingController = widget.controller ?? TextEditingController(); } + if (oldWidget.enableFilter != widget.enableFilter) { + if (!widget.enableFilter) { + _enableFilter = false; + } + } if (oldWidget.enableSearch != widget.enableSearch) { if (!widget.enableSearch) { currentHighlight = null; @@ -676,6 +680,7 @@ class _DropdownMenuState extends State> { ); currentHighlight = widget.enableSearch ? i : null; widget.onSelected?.call(entry.value); + _enableFilter = false; } : null, requestFocusOnHover: false, @@ -748,6 +753,8 @@ class _DropdownMenuState extends State> { if (_enableFilter) { filteredEntries = widget.filterCallback?.call(filteredEntries, _localTextEditingController!.text) ?? filter(widget.dropdownMenuEntries, _localTextEditingController!); + } else { + filteredEntries = widget.dropdownMenuEntries; } if (widget.enableSearch) { diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index 4227ac9ed2..2bcbc9e20c 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -2524,6 +2524,49 @@ void main() { expect(menuAnchor.alignmentOffset, alignmentOffset); }); + + testWidgets('DropdownMenu filter is disabled until text input', (WidgetTester tester) async{ + await tester.pumpWidget(MaterialApp( + home: Scaffold( + body: DropdownMenu( + requestFocusOnTap: true, + enableFilter: true, + initialSelection: menuChildren[0].value, + dropdownMenuEntries: menuChildren, + ), + ), + )); + + await tester.tap(find.byType(DropdownMenu)); + await tester.pumpAndSettle(); + + // All entries should be available, and two buttons should be found for each entry. + // One is layout for the _DropdownMenuBody, the other one is the real button item in the menu. + for (final TestMenu menu in TestMenu.values) { + expect(find.widgetWithText(MenuItemButton, menu.label), findsNWidgets(2)); + } + + // Text input would enable the filter. + await tester.enterText(find.byType(TextField).first, 'Menu 1'); + await tester.pumpAndSettle(); + for (final TestMenu menu in TestMenu.values) { + // 'Menu 1' should be 2, other items should only find one. + if (menu.label == TestMenu.mainMenu1.label) { + expect(find.widgetWithText(MenuItemButton, menu.label), findsNWidgets(2)); + } else { + expect(find.widgetWithText(MenuItemButton, menu.label), findsOneWidget); + } + } + + // Selecting an item would disable filter again. + await tester.tap(find.widgetWithText(MenuItemButton, 'Menu 1').last); + await tester.pumpAndSettle(); + await tester.tap(find.byType(DropdownMenu)); + await tester.pumpAndSettle(); + for (final TestMenu menu in TestMenu.values) { + expect(find.widgetWithText(MenuItemButton, menu.label), findsNWidgets(2)); + } + }); } enum TestMenu {