Skip to content

Commit 5805f45

Browse files
authored
Fix autocomplete selections (#109185)
1 parent 7dbe57d commit 5805f45

2 files changed

Lines changed: 98 additions & 23 deletions

File tree

packages/flutter/lib/src/widgets/autocomplete.dart

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'framework.dart';
1616
import 'inherited_notifier.dart';
1717
import 'overlay.dart';
1818
import 'shortcuts.dart';
19+
import 'tap_region.dart';
1920

2021
/// The type of the [RawAutocomplete] callback which computes the list of
2122
/// optional completions for the widget's field, based on the text the user has
@@ -421,13 +422,15 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
421422
link: _optionsLayerLink,
422423
showWhenUnlinked: false,
423424
targetAnchor: Alignment.bottomLeft,
424-
child: AutocompleteHighlightedOption(
425-
highlightIndexNotifier: _highlightedOptionIndex,
426-
child: Builder(
427-
builder: (BuildContext context) {
428-
return widget.optionsViewBuilder(context, _select, _options);
429-
}
430-
)
425+
child: TextFieldTapRegion(
426+
child: AutocompleteHighlightedOption(
427+
highlightIndexNotifier: _highlightedOptionIndex,
428+
child: Builder(
429+
builder: (BuildContext context) {
430+
return widget.optionsViewBuilder(context, _select, _options);
431+
}
432+
)
433+
),
431434
),
432435
);
433436
},
@@ -527,22 +530,24 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
527530

528531
@override
529532
Widget build(BuildContext context) {
530-
return Container(
531-
key: _fieldKey,
532-
child: Shortcuts(
533-
shortcuts: _shortcuts,
534-
child: Actions(
535-
actions: _actionMap,
536-
child: CompositedTransformTarget(
537-
link: _optionsLayerLink,
538-
child: widget.fieldViewBuilder == null
539-
? const SizedBox.shrink()
540-
: widget.fieldViewBuilder!(
541-
context,
542-
_textEditingController,
543-
_focusNode,
544-
_onFieldSubmitted,
545-
),
533+
return TextFieldTapRegion(
534+
child: Container(
535+
key: _fieldKey,
536+
child: Shortcuts(
537+
shortcuts: _shortcuts,
538+
child: Actions(
539+
actions: _actionMap,
540+
child: CompositedTransformTarget(
541+
link: _optionsLayerLink,
542+
child: widget.fieldViewBuilder == null
543+
? const SizedBox.shrink()
544+
: widget.fieldViewBuilder!(
545+
context,
546+
_textEditingController,
547+
_focusNode,
548+
_onFieldSubmitted,
549+
),
550+
),
546551
),
547552
),
548553
),

packages/flutter/test/widgets/autocomplete_test.dart

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,76 @@ void main() {
129129
expect(lastOptions.elementAt(5), 'northern white rhinoceros');
130130
});
131131

132+
testWidgets('tapping on an option selects it', (WidgetTester tester) async {
133+
final GlobalKey fieldKey = GlobalKey();
134+
final GlobalKey optionsKey = GlobalKey();
135+
late Iterable<String> lastOptions;
136+
late FocusNode focusNode;
137+
late TextEditingController textEditingController;
138+
139+
await tester.pumpWidget(
140+
MaterialApp(
141+
home: Scaffold(
142+
body: RawAutocomplete<String>(
143+
optionsBuilder: (TextEditingValue textEditingValue) {
144+
return kOptions.where((String option) {
145+
return option.contains(textEditingValue.text.toLowerCase());
146+
});
147+
},
148+
fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
149+
focusNode = fieldFocusNode;
150+
textEditingController = fieldTextEditingController;
151+
return TextField(
152+
key: fieldKey,
153+
focusNode: focusNode,
154+
controller: textEditingController,
155+
);
156+
},
157+
optionsViewBuilder: (BuildContext context, AutocompleteOnSelected<String> onSelected, Iterable<String> options) {
158+
lastOptions = options;
159+
return Material(
160+
elevation: 4.0,
161+
child: ListView.builder(
162+
key: optionsKey,
163+
padding: const EdgeInsets.all(8.0),
164+
itemCount: options.length,
165+
itemBuilder: (BuildContext context, int index) {
166+
final String option = options.elementAt(index);
167+
return GestureDetector(
168+
onTap: () {
169+
onSelected(option);
170+
},
171+
child: ListTile(
172+
title: Text(option),
173+
),
174+
);
175+
},
176+
),
177+
);
178+
},
179+
),
180+
),
181+
),
182+
);
183+
184+
// The field is always rendered, but the options are not unless needed.
185+
expect(find.byKey(fieldKey), findsOneWidget);
186+
expect(find.byKey(optionsKey), findsNothing);
187+
188+
// Tap on the text field to open the options.
189+
await tester.tap(find.byKey(fieldKey));
190+
await tester.pump();
191+
expect(find.byKey(optionsKey), findsOneWidget);
192+
expect(lastOptions.length, kOptions.length);
193+
194+
await tester.tap(find.text(kOptions[2]));
195+
await tester.pump();
196+
197+
expect(find.byKey(optionsKey), findsNothing);
198+
199+
expect(textEditingController.text, equals(kOptions[2]));
200+
});
201+
132202
testWidgets('can filter and select a list of custom User options', (WidgetTester tester) async {
133203
final GlobalKey fieldKey = GlobalKey();
134204
final GlobalKey optionsKey = GlobalKey();

0 commit comments

Comments
 (0)