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
This commit is contained in:
PurplePolyhedron 2024-08-15 02:06:14 +08:00 committed by GitHub
parent 6ef1156f7e
commit b5847d364a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 52 additions and 2 deletions

View File

@ -488,7 +488,7 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
final GlobalKey _leadingKey = GlobalKey();
late List<GlobalKey> buttonItemKeys;
final MenuController _controller = MenuController();
late bool _enableFilter;
bool _enableFilter = false;
late List<DropdownMenuEntry<T>> filteredEntries;
List<Widget>? _initialMenu;
int? currentHighlight;
@ -504,7 +504,6 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
} else {
_localTextEditingController = TextEditingController();
}
_enableFilter = widget.enableFilter;
filteredEntries = widget.dropdownMenuEntries;
buttonItemKeys = List<GlobalKey>.generate(filteredEntries.length, (int index) => GlobalKey());
_menuHasEnabledItem = filteredEntries.any((DropdownMenuEntry<T> entry) => entry.enabled);
@ -537,6 +536,11 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
}
_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<T> extends State<DropdownMenu<T>> {
);
currentHighlight = widget.enableSearch ? i : null;
widget.onSelected?.call(entry.value);
_enableFilter = false;
}
: null,
requestFocusOnHover: false,
@ -748,6 +753,8 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
if (_enableFilter) {
filteredEntries = widget.filterCallback?.call(filteredEntries, _localTextEditingController!.text)
?? filter(widget.dropdownMenuEntries, _localTextEditingController!);
} else {
filteredEntries = widget.dropdownMenuEntries;
}
if (widget.enableSearch) {

View File

@ -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<TestMenu>(
requestFocusOnTap: true,
enableFilter: true,
initialSelection: menuChildren[0].value,
dropdownMenuEntries: menuChildren,
),
),
));
await tester.tap(find.byType(DropdownMenu<TestMenu>));
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<TestMenu>));
await tester.pumpAndSettle();
for (final TestMenu menu in TestMenu.values) {
expect(find.widgetWithText(MenuItemButton, menu.label), findsNWidgets(2));
}
});
}
enum TestMenu {