From 92a335e465f0615844f03c2ecb8993d7db6cf11d Mon Sep 17 00:00:00 2001 From: jimshepherd <31232524+jimshepherd@users.noreply.github.com> Date: Fri, 7 Feb 2020 18:50:37 -0500 Subject: [PATCH] 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. --- .../flutter/lib/src/material/dropdown.dart | 7 ++- .../flutter/test/material/dropdown_test.dart | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 2cedb414fe2..d13a90ef559 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -1427,19 +1427,20 @@ class DropdownButtonFormField extends FormField { validator: validator, autovalidate: autovalidate, builder: (FormFieldState field) { + final _DropdownButtonFormFieldState state = field as _DropdownButtonFormFieldState; 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( - 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, diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart index 795822c42c4..e00add869dd 100644 --- a/packages/flutter/test/material/dropdown_test.dart +++ b/packages/flutter/test/material/dropdown_test.dart @@ -360,6 +360,63 @@ void main() { } }); + testWidgets('Dropdown form field uses form field state', (WidgetTester tester) async { + final Key buttonKey = UniqueKey(); + final GlobalKey formKey = GlobalKey(); + String value; + await tester.pumpWidget( + StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return MaterialApp( + home: Material( + child: Form( + key: formKey, + child: DropdownButtonFormField( + key: buttonKey, + value: value, + hint: const Text('Select Value'), + decoration: const InputDecoration( + prefixIcon: Icon(Icons.fastfood) + ), + items: menuItems.map((String val) { + return DropdownMenuItem( + 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,