Autocomplete and RawAutocomplete initialValue parameter (#80257)
This commit is contained in:
parent
a4411b5565
commit
884129d156
@ -167,6 +167,7 @@ class Autocomplete<T extends Object> extends StatelessWidget {
|
||||
this.fieldViewBuilder = _defaultFieldViewBuilder,
|
||||
this.onSelected,
|
||||
this.optionsViewBuilder,
|
||||
this.initialValue,
|
||||
}) : assert(displayStringForOption != null),
|
||||
assert(optionsBuilder != null),
|
||||
super(key: key);
|
||||
@ -192,6 +193,9 @@ class Autocomplete<T extends Object> extends StatelessWidget {
|
||||
/// default.
|
||||
final AutocompleteOptionsViewBuilder<T>? optionsViewBuilder;
|
||||
|
||||
/// {@macro flutter.widgets.RawAutocomplete.initialValue}
|
||||
final TextEditingValue? initialValue;
|
||||
|
||||
static Widget _defaultFieldViewBuilder(BuildContext context, TextEditingController textEditingController, FocusNode focusNode, VoidCallback onFieldSubmitted) {
|
||||
return _AutocompleteField(
|
||||
focusNode: focusNode,
|
||||
@ -205,6 +209,7 @@ class Autocomplete<T extends Object> extends StatelessWidget {
|
||||
return RawAutocomplete<T>(
|
||||
displayStringForOption: displayStringForOption,
|
||||
fieldViewBuilder: fieldViewBuilder,
|
||||
initialValue: initialValue,
|
||||
optionsBuilder: optionsBuilder,
|
||||
optionsViewBuilder: optionsViewBuilder ?? (BuildContext context, AutocompleteOnSelected<T> onSelected, Iterable<T> options) {
|
||||
return _AutocompleteOptions<T>(
|
||||
|
@ -493,6 +493,7 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
|
||||
this.focusNode,
|
||||
this.onSelected,
|
||||
this.textEditingController,
|
||||
this.initialValue,
|
||||
}) : assert(displayStringForOption != null),
|
||||
assert(
|
||||
fieldViewBuilder != null
|
||||
@ -502,6 +503,10 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
|
||||
assert(optionsBuilder != null),
|
||||
assert(optionsViewBuilder != null),
|
||||
assert((focusNode == null) == (textEditingController == null)),
|
||||
assert(
|
||||
!(textEditingController != null && initialValue != null),
|
||||
'textEditingController and initialValue cannot be simultaneously defined.'
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
/// {@template flutter.widgets.RawAutocomplete.fieldViewBuilder}
|
||||
@ -661,6 +666,16 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
|
||||
/// If this parameter is not null, then [focusNode] must also be not null.
|
||||
final TextEditingController? textEditingController;
|
||||
|
||||
/// {@template flutter.widgets.RawAutocomplete.initialValue}
|
||||
/// The initial value to use for the text field.
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// Setting the initial value does not notify [textEditingController]'s
|
||||
/// listeners, and thus will not cause the options UI to appear.
|
||||
///
|
||||
/// This parameter is ignored if [textEditingController] is defined.
|
||||
final TextEditingValue? initialValue;
|
||||
|
||||
/// Calls [AutocompleteFieldViewBuilder]'s onFieldSubmitted callback for the
|
||||
/// RawAutocomplete widget indicated by the given [GlobalKey].
|
||||
///
|
||||
@ -811,7 +826,7 @@ class _RawAutocompleteState<T extends Object> extends State<RawAutocomplete<T>>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_textEditingController = widget.textEditingController ?? TextEditingController();
|
||||
_textEditingController = widget.textEditingController ?? TextEditingController.fromValue(widget.initialValue);
|
||||
_textEditingController.addListener(_onChangedField);
|
||||
_focusNode = widget.focusNode ?? FocusNode();
|
||||
_focusNode.addListener(_onChangedFocus);
|
||||
|
@ -253,4 +253,51 @@ void main() {
|
||||
await tester.pump();
|
||||
expect(find.byKey(optionsKey), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('initialValue sets initial text field value', (WidgetTester tester) async {
|
||||
late String lastSelection;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Autocomplete<String>(
|
||||
initialValue: const TextEditingValue(text: 'lem'),
|
||||
onSelected: (String selection) {
|
||||
lastSelection = selection;
|
||||
},
|
||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||
return kOptions.where((String option) {
|
||||
return option.contains(textEditingValue.text.toLowerCase());
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// The field is always rendered, but the options are not unless needed.
|
||||
expect(find.byType(TextFormField), findsOneWidget);
|
||||
expect(find.byType(ListView), findsNothing);
|
||||
expect(
|
||||
tester.widget<TextFormField>(find.byType(TextFormField)).controller!.text,
|
||||
'lem',
|
||||
);
|
||||
|
||||
// Focus the empty field. All the options are displayed.
|
||||
await tester.tap(find.byType(TextFormField));
|
||||
await tester.pump();
|
||||
expect(find.byType(ListView), findsOneWidget);
|
||||
final ListView list = find.byType(ListView).evaluate().first.widget as ListView;
|
||||
// Displays just one option ('lemur').
|
||||
expect(list.semanticChildCount, 1);
|
||||
|
||||
// Select a option. The options hide and the field updates to show the
|
||||
// selection.
|
||||
await tester.tap(find.byType(InkWell).first);
|
||||
await tester.pump();
|
||||
expect(find.byType(TextFormField), findsOneWidget);
|
||||
expect(find.byType(ListView), findsNothing);
|
||||
final TextFormField field = find.byType(TextFormField).evaluate().first.widget as TextFormField;
|
||||
expect(field.controller!.text, 'lemur');
|
||||
expect(lastSelection, 'lemur');
|
||||
});
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ void main() {
|
||||
expect(lastOptions.elementAt(0), 'chameleon');
|
||||
expect(lastOptions.elementAt(1), 'elephant');
|
||||
|
||||
// Select a option. The options hide and the field updates to show the
|
||||
// Select an option. The options hide and the field updates to show the
|
||||
// selection.
|
||||
final String selection = lastOptions.elementAt(1);
|
||||
lastOnSelected(selection);
|
||||
@ -184,7 +184,7 @@ void main() {
|
||||
expect(lastOptions.elementAt(0), kOptionsUsers[0]);
|
||||
expect(lastOptions.elementAt(1), kOptionsUsers[1]);
|
||||
|
||||
// Select a option. The options hide and onSelected is called.
|
||||
// Select an option. The options hide and onSelected is called.
|
||||
final User selection = lastOptions.elementAt(1);
|
||||
lastOnSelected(selection);
|
||||
await tester.pump();
|
||||
@ -266,7 +266,7 @@ void main() {
|
||||
expect(lastOptions.elementAt(0), kOptionsUsers[0]);
|
||||
expect(lastOptions.elementAt(1), kOptionsUsers[1]);
|
||||
|
||||
// Select a option. The options hide and onSelected is called. The field
|
||||
// Select an option. The options hide and onSelected is called. The field
|
||||
// has its text set to the selection's display string.
|
||||
final User selection = lastOptions.elementAt(1);
|
||||
lastOnSelected(selection);
|
||||
@ -553,4 +553,96 @@ void main() {
|
||||
expect(find.byKey(optionsKey), findsNothing);
|
||||
expect(textEditingController.text, lastOptions.elementAt(0));
|
||||
});
|
||||
|
||||
testWidgets('initialValue sets initial text field value', (WidgetTester tester) async {
|
||||
final GlobalKey fieldKey = GlobalKey();
|
||||
final GlobalKey optionsKey = GlobalKey();
|
||||
late Iterable<String> lastOptions;
|
||||
late AutocompleteOnSelected<String> lastOnSelected;
|
||||
late FocusNode focusNode;
|
||||
late TextEditingController textEditingController;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: RawAutocomplete<String>(
|
||||
// Should initialize text field with 'lem'.
|
||||
initialValue: const TextEditingValue(text: 'lem'),
|
||||
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;
|
||||
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);
|
||||
// The text editing controller value starts off with initialized value.
|
||||
expect(textEditingController.text, 'lem');
|
||||
|
||||
// Focus the empty field. All the options are displayed.
|
||||
focusNode.requestFocus();
|
||||
await tester.pump();
|
||||
expect(find.byKey(optionsKey), findsOneWidget);
|
||||
expect(lastOptions.elementAt(0), 'lemur');
|
||||
|
||||
// Select an option. The options hide and the field updates to show the
|
||||
// selection.
|
||||
final String selection = lastOptions.elementAt(0);
|
||||
lastOnSelected(selection);
|
||||
await tester.pump();
|
||||
expect(find.byKey(fieldKey), findsOneWidget);
|
||||
expect(find.byKey(optionsKey), findsNothing);
|
||||
expect(textEditingController.text, selection);
|
||||
});
|
||||
|
||||
testWidgets('initialValue cannot be defined if TextEditingController is defined', (WidgetTester tester) async {
|
||||
final FocusNode focusNode = FocusNode();
|
||||
final TextEditingController textEditingController = TextEditingController();
|
||||
|
||||
expect(
|
||||
() {
|
||||
RawAutocomplete<String>(
|
||||
focusNode: focusNode,
|
||||
// Both [initialValue] and [textEditingController] cannot be
|
||||
// simultaneously defined.
|
||||
initialValue: const TextEditingValue(text: 'lemur'),
|
||||
textEditingController: textEditingController,
|
||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||
return kOptions.where((String option) {
|
||||
return option.contains(textEditingValue.text.toLowerCase());
|
||||
});
|
||||
},
|
||||
optionsViewBuilder: (BuildContext context, AutocompleteOnSelected<String> onSelected, Iterable<String> options) {
|
||||
return Container();
|
||||
},
|
||||
fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
|
||||
return TextField(
|
||||
focusNode: focusNode,
|
||||
controller: textEditingController,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
throwsAssertionError,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user