Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to require a Dropdown selection as part of form validation in Flutter?

Tags:

flutter

dart

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/

like image 449
Greg Noe Avatar asked Oct 05 '18 15:10

Greg Noe


People also ask

How do you validate a form dropdown in Flutter?

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 ?

How do I select a dropdown in Flutter?

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.


2 Answers

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;

like image 143
Andres R Avatar answered Nov 03 '22 02:11

Andres R


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).

like image 42
Edmund Tam Avatar answered Nov 03 '22 01:11

Edmund Tam