I'm familiar with form validation using a TextFormField in Flutter, but is it possible to integrate a DropdownButton into a Form and require one of its value be selected before submission?
Basically, integrate DropdownButton validation into this basic Flutter validation example:
https://flutter.io/cookbook/forms/validation/
DropdownButtonFormField<String>( items: [ DropdownMenuItem<String>( value: "January", child: Text( "Male", ), DropdownMenuItem<String>( value: "February", child: Text( "Female", ), ], onChanged: (value) async { setState(() { gender= value; }); } value: gender, validator: (value) => value == null ?
The default value shows the currently selected value. We can even include a down arrow icon on the list. On clicking the DropDownButton it opens a list of items, from which the user can select the desired option.
Dart Package have already the widget DropdownButtonFormField for this. Here is an example of how to use it:
List<String> typeNeg = [
"One",
"Two",
"Three",];
String dropdownValue = "One";
DropdownButtonFormField<String>(
value: dropdownValue,
hint: Text("Type of business"),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
validator: (String value) {
if (value?.isEmpty ?? true) {
return 'Please enter a valid type of business';
}
},
items: typeNeg
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onSaved: (val) => setState(() => _user.typeNeg = val),
),
The user model is as follows:
class User {
int id;
String name;
String email;
String typeNeg;
User({this.id, this.name, this.email, this.typeNeg});
factory User.fromJson(Map<String, dynamic> parsedJson) {
return User(
id: parsedJson["id"],
name: parsedJson["name"] as String,
email: parsedJson["email"] as String,
typeNeg: parsedJson["typeNeg"] as String,
);
}
save(){
print("User saved");
}
}
To try the validator option change String dropdownValue = "One";
to String dropdownValue = null;
From text_form_field.dart
file in Flutter's source code you can see that TextFormField
is no more than a FormField
emitting a TextField
widget in its builder
callback. You can write your own DropdownFormField
using a similar pattern. Here's mine:
import 'package:flutter/material.dart';
class DropdownFormField<T> extends FormField<T> {
DropdownFormField({
Key key,
InputDecoration decoration,
T initialValue,
List<DropdownMenuItem<T>> items,
bool autovalidate = false,
FormFieldSetter<T> onSaved,
FormFieldValidator<T> validator,
}) : super(
key: key,
onSaved: onSaved,
validator: validator,
autovalidate: autovalidate,
initialValue: items.contains(initialValue) ? initialValue : null,
builder: (FormFieldState<T> field) {
final InputDecoration effectiveDecoration = (decoration ?? const InputDecoration())
.applyDefaults(Theme.of(field.context).inputDecorationTheme);
return InputDecorator(
decoration:
effectiveDecoration.copyWith(errorText: field.hasError ? field.errorText : null),
isEmpty: field.value == '' || field.value == null,
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(
value: field.value,
isDense: true,
onChanged: field.didChange,
items: items.toList(),
),
),
);
},
);
}
The key is to bind DropdownButton
's onChanged
to field.didChange
. Usage is pretty straightforward:
DropdownFormField<String>(
validator: (value) {
if (value == null) {
return 'Required';
}
},
onSaved: (value) {
// ...
},
decoration: InputDecoration(
border: UnderlineInputBorder(),
filled: true,
labelText: 'Demo',
),
initialValue: null,
items: [
DropdownMenuItem<String>(
value: '1',
child: Text('1'),
),
DropdownMenuItem<String>(
value: '2',
child: Text('2'),
)
],
),
I got the idea from this site. The difference is that my version of DropdownFormField
is closer to Flutter's native implementation (which extends TextFormField
instead of wrapping it inside a StatefulWidget
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With