Fix autocomplete selections (#109185)
This commit is contained in:
parent
7dbe57d454
commit
5805f45445
@ -16,6 +16,7 @@ import 'framework.dart';
|
|||||||
import 'inherited_notifier.dart';
|
import 'inherited_notifier.dart';
|
||||||
import 'overlay.dart';
|
import 'overlay.dart';
|
||||||
import 'shortcuts.dart';
|
import 'shortcuts.dart';
|
||||||
|
import 'tap_region.dart';
|
||||||
|
|
||||||
/// The type of the [RawAutocomplete] callback which computes the list of
|
/// The type of the [RawAutocomplete] callback which computes the list of
|
||||||
/// optional completions for the widget's field, based on the text the user has
|
/// 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>>
|
|||||||
link: _optionsLayerLink,
|
link: _optionsLayerLink,
|
||||||
showWhenUnlinked: false,
|
showWhenUnlinked: false,
|
||||||
targetAnchor: Alignment.bottomLeft,
|
targetAnchor: Alignment.bottomLeft,
|
||||||
child: AutocompleteHighlightedOption(
|
child: TextFieldTapRegion(
|
||||||
highlightIndexNotifier: _highlightedOptionIndex,
|
child: AutocompleteHighlightedOption(
|
||||||
child: Builder(
|
highlightIndexNotifier: _highlightedOptionIndex,
|
||||||
builder: (BuildContext context) {
|
child: Builder(
|
||||||
return widget.optionsViewBuilder(context, _select, _options);
|
builder: (BuildContext context) {
|
||||||
}
|
return widget.optionsViewBuilder(context, _select, _options);
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -527,22 +530,24 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return TextFieldTapRegion(
|
||||||
key: _fieldKey,
|
child: Container(
|
||||||
child: Shortcuts(
|
key: _fieldKey,
|
||||||
shortcuts: _shortcuts,
|
child: Shortcuts(
|
||||||
child: Actions(
|
shortcuts: _shortcuts,
|
||||||
actions: _actionMap,
|
child: Actions(
|
||||||
child: CompositedTransformTarget(
|
actions: _actionMap,
|
||||||
link: _optionsLayerLink,
|
child: CompositedTransformTarget(
|
||||||
child: widget.fieldViewBuilder == null
|
link: _optionsLayerLink,
|
||||||
? const SizedBox.shrink()
|
child: widget.fieldViewBuilder == null
|
||||||
: widget.fieldViewBuilder!(
|
? const SizedBox.shrink()
|
||||||
context,
|
: widget.fieldViewBuilder!(
|
||||||
_textEditingController,
|
context,
|
||||||
_focusNode,
|
_textEditingController,
|
||||||
_onFieldSubmitted,
|
_focusNode,
|
||||||
),
|
_onFieldSubmitted,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -129,6 +129,76 @@ void main() {
|
|||||||
expect(lastOptions.elementAt(5), 'northern white rhinoceros');
|
expect(lastOptions.elementAt(5), 'northern white rhinoceros');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('tapping on an option selects it', (WidgetTester tester) async {
|
||||||
|
final GlobalKey fieldKey = GlobalKey();
|
||||||
|
final GlobalKey optionsKey = GlobalKey();
|
||||||
|
late Iterable<String> lastOptions;
|
||||||
|
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) {
|
||||||
|
lastOptions = options;
|
||||||
|
return Material(
|
||||||
|
elevation: 4.0,
|
||||||
|
child: ListView.builder(
|
||||||
|
key: optionsKey,
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
itemCount: options.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
final String option = options.elementAt(index);
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
onSelected(option);
|
||||||
|
},
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(option),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// The field is always rendered, but the options are not unless needed.
|
||||||
|
expect(find.byKey(fieldKey), findsOneWidget);
|
||||||
|
expect(find.byKey(optionsKey), findsNothing);
|
||||||
|
|
||||||
|
// Tap on the text field to open the options.
|
||||||
|
await tester.tap(find.byKey(fieldKey));
|
||||||
|
await tester.pump();
|
||||||
|
expect(find.byKey(optionsKey), findsOneWidget);
|
||||||
|
expect(lastOptions.length, kOptions.length);
|
||||||
|
|
||||||
|
await tester.tap(find.text(kOptions[2]));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.byKey(optionsKey), findsNothing);
|
||||||
|
|
||||||
|
expect(textEditingController.text, equals(kOptions[2]));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('can filter and select a list of custom User options', (WidgetTester tester) async {
|
testWidgets('can filter and select a list of custom User options', (WidgetTester tester) async {
|
||||||
final GlobalKey fieldKey = GlobalKey();
|
final GlobalKey fieldKey = GlobalKey();
|
||||||
final GlobalKey optionsKey = GlobalKey();
|
final GlobalKey optionsKey = GlobalKey();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user