Unverified Commit 69b92f04 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Hide autocomplete menu on select. (#100251)

parent ef912b8d
...@@ -316,6 +316,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -316,6 +316,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
_userHidOptions = false; _userHidOptions = false;
_lastFieldText = value.text; _lastFieldText = value.text;
} }
_updateActions();
_updateOverlay(); _updateOverlay();
} }
...@@ -323,6 +324,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -323,6 +324,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
void _onChangedFocus() { void _onChangedFocus() {
// Options should no longer be hidden when the field is re-focused. // Options should no longer be hidden when the field is re-focused.
_userHidOptions = !_focusNode.hasFocus; _userHidOptions = !_focusNode.hasFocus;
_updateActions();
_updateOverlay(); _updateOverlay();
} }
...@@ -345,6 +347,8 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -345,6 +347,8 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
selection: TextSelection.collapsed(offset: selectionString.length), selection: TextSelection.collapsed(offset: selectionString.length),
text: selectionString, text: selectionString,
); );
_updateActions();
_updateOverlay();
widget.onSelected?.call(_selection!); widget.onSelected?.call(_selection!);
} }
...@@ -355,6 +359,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -355,6 +359,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
void _highlightPreviousOption(AutocompletePreviousOptionIntent intent) { void _highlightPreviousOption(AutocompletePreviousOptionIntent intent) {
if (_userHidOptions) { if (_userHidOptions) {
_userHidOptions = false; _userHidOptions = false;
_updateActions();
_updateOverlay(); _updateOverlay();
return; return;
} }
...@@ -364,6 +369,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -364,6 +369,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
void _highlightNextOption(AutocompleteNextOptionIntent intent) { void _highlightNextOption(AutocompleteNextOptionIntent intent) {
if (_userHidOptions) { if (_userHidOptions) {
_userHidOptions = false; _userHidOptions = false;
_updateActions();
_updateOverlay(); _updateOverlay();
return; return;
} }
...@@ -373,6 +379,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -373,6 +379,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
Object? _hideOptions(DismissIntent intent) { Object? _hideOptions(DismissIntent intent) {
if (!_userHidOptions) { if (!_userHidOptions) {
_userHidOptions = true; _userHidOptions = true;
_updateActions();
_updateOverlay(); _updateOverlay();
return null; return null;
} }
...@@ -389,12 +396,27 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -389,12 +396,27 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
_hideOptionsAction.enabled = enabled; _hideOptionsAction.enabled = enabled;
} }
void _updateActions() {
_setActionsEnabled(_focusNode.hasFocus && _selection == null && _options.isNotEmpty);
}
bool _floatingOptionsUpdateScheduled = false;
// Hide or show the options overlay, if needed. // Hide or show the options overlay, if needed.
void _updateOverlay() { void _updateOverlay() {
_setActionsEnabled(_focusNode.hasFocus && _selection == null && _options.isNotEmpty); if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
if (!_floatingOptionsUpdateScheduled) {
_floatingOptionsUpdateScheduled = true;
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
_floatingOptionsUpdateScheduled = false;
_updateOverlay();
});
}
return;
}
_floatingOptions?.remove();
if (_shouldShowOptions) { if (_shouldShowOptions) {
_floatingOptions?.remove(); final OverlayEntry newFloatingOptions = OverlayEntry(
_floatingOptions = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
return CompositedTransformFollower( return CompositedTransformFollower(
link: _optionsLayerLink, link: _optionsLayerLink,
...@@ -411,9 +433,9 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -411,9 +433,9 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
); );
}, },
); );
Overlay.of(context, rootOverlay: true)!.insert(_floatingOptions!); Overlay.of(context, rootOverlay: true)!.insert(newFloatingOptions);
} else if (_floatingOptions != null) { _floatingOptions = newFloatingOptions;
_floatingOptions!.remove(); } else {
_floatingOptions = null; _floatingOptions = null;
} }
} }
...@@ -473,9 +495,8 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -473,9 +495,8 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
AutocompleteNextOptionIntent: _nextOptionAction, AutocompleteNextOptionIntent: _nextOptionAction,
DismissIntent: _hideOptionsAction, DismissIntent: _hideOptionsAction,
}; };
SchedulerBinding.instance.addPostFrameCallback((Duration _) { _updateActions();
_updateOverlay(); _updateOverlay();
});
} }
@override @override
...@@ -486,9 +507,8 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>> ...@@ -486,9 +507,8 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
widget.textEditingController, widget.textEditingController,
); );
_updateFocusNode(oldWidget.focusNode, widget.focusNode); _updateFocusNode(oldWidget.focusNode, widget.focusNode);
SchedulerBinding.instance.addPostFrameCallback((Duration _) { _updateActions();
_updateOverlay(); _updateOverlay();
});
} }
@override @override
......
...@@ -1023,4 +1023,53 @@ void main() { ...@@ -1023,4 +1023,53 @@ void main() {
expect(lastHighlighted, 5); expect(lastHighlighted, 5);
}); });
testWidgets('floating menu goes away on select', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/99749.
final GlobalKey fieldKey = GlobalKey();
final GlobalKey optionsKey = GlobalKey();
late AutocompleteOnSelected<String> lastOnSelected;
late FocusNode focusNode;
late TextEditingController textEditingController;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: RawAutocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
return kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
focusNode = fieldFocusNode;
textEditingController = fieldTextEditingController;
return TextField(
key: fieldKey,
focusNode: focusNode,
controller: textEditingController,
);
},
optionsViewBuilder: (BuildContext context, AutocompleteOnSelected<String> onSelected, Iterable<String> options) {
lastOnSelected = onSelected;
return Container(key: optionsKey);
},
),
),
),
);
// The field is always rendered, but the options are not unless needed.
expect(find.byKey(fieldKey), findsOneWidget);
expect(find.byKey(optionsKey), findsNothing);
await tester.enterText(find.byKey(fieldKey), kOptions[0]);
await tester.pumpAndSettle();
expect(find.byKey(optionsKey), findsOneWidget);
// Pretend that the only option is selected. This does not change the
// text in the text field.
lastOnSelected(kOptions[0]);
await tester.pump();
expect(find.byKey(optionsKey), findsNothing);
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment