Use state value in DropdownButtonFieldForm (#37145)

The current implementation of DropdownButtonFormField does not pass the initial value to _DropdownButtonFormFieldState. As a result changes made through the child DropdownButton are not made to the FormFieldState and the widget is not updated unless a onChanged function is provided to the DropdownButtomFormField constructor.

This change modifies DropdownButtonFormField to behave more consistently with other FormField widgets in how the Form state is handled.
This commit is contained in:
jimshepherd 2020-02-07 18:50:37 -05:00 committed by GitHub
parent 082ae838bd
commit 92a335e465
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 3 deletions

View File

@ -1427,19 +1427,20 @@ class DropdownButtonFormField<T> extends FormField<T> {
validator: validator,
autovalidate: autovalidate,
builder: (FormFieldState<T> field) {
final _DropdownButtonFormFieldState<T> state = field as _DropdownButtonFormFieldState<T>;
final InputDecoration effectiveDecoration = decoration.applyDefaults(
Theme.of(field.context).inputDecorationTheme,
);
return InputDecorator(
decoration: effectiveDecoration.copyWith(errorText: field.errorText),
isEmpty: value == null,
isEmpty: state.value == null,
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(
value: value,
value: state.value,
items: items,
selectedItemBuilder: selectedItemBuilder,
hint: hint,
onChanged: onChanged == null ? null : field.didChange,
onChanged: onChanged == null ? null : state.didChange,
disabledHint: disabledHint,
elevation: elevation,
style: style,

View File

@ -360,6 +360,63 @@ void main() {
}
});
testWidgets('Dropdown form field uses form field state', (WidgetTester tester) async {
final Key buttonKey = UniqueKey();
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
String value;
await tester.pumpWidget(
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return MaterialApp(
home: Material(
child: Form(
key: formKey,
child: DropdownButtonFormField<String>(
key: buttonKey,
value: value,
hint: const Text('Select Value'),
decoration: const InputDecoration(
prefixIcon: Icon(Icons.fastfood)
),
items: menuItems.map((String val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val)
);
}).toList(),
validator: (String v) => v == null ? 'Must select value' : null,
onChanged: (String newValue) {},
onSaved: (String v) {
setState(() {
value = v;
});
},
),
),
),
);
}
)
);
int getIndex() {
final IndexedStack stack = tester.element(find.byType(IndexedStack)).widget as IndexedStack;
return stack.index;
}
// Initial value of null displays hint
expect(value, equals(null));
expect(getIndex(), 4);
await tester.tap(find.text('Select Value'));
await tester.pumpAndSettle();
await tester.tap(find.text('three').last);
await tester.pumpAndSettle();
expect(getIndex(), 2);
// Changes only made to FormField state until form saved
expect(value, equals(null));
final FormState form = formKey.currentState;
form.save();
expect(value, equals('three'));
});
testWidgets('Dropdown in ListView', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/12053
// Positions a DropdownButton at the left and right edges of the screen,