Form builder fix - A package helps in creation of data collection forms in Flutter by removing the boilerplate needed to build a form

Overview

Flutter FormBuilder - flutter_form_builder

This package helps in creation of data collection forms in Flutter by removing the boilerplate needed to build a form, validate fields, react to changes, and collect final user input.

Also included are common ready-made form input fields for FormBuilder. This gives you a convenient way of adding common ready-made input fields instead of creating your own FormBuilderField from scratch.


Pub Version GitHub Workflow Status Codecov CodeFactor Grade GitHub OSS Lifecycle

Buy me a coffee


Example

import 'package:flutter_form_builder/flutter_form_builder.dart';

...

final _formKey = GlobalKey<FormBuilderState>();

...

@override
Widget build(BuildContext context) {
  return Column(
    children: <Widget>[
      FormBuilder(
        key: _formKey,
        autovalidate: true,
        child: Column(
          children: <Widget>[
            FormBuilderFilterChip(
              name: 'filter_chip',
              decoration: InputDecoration(
                labelText: 'Select many options',
              ),
              options: [
                FormBuilderFieldOption(
                    value: 'Test', child: Text('Test')),
                FormBuilderFieldOption(
                    value: 'Test 1', child: Text('Test 1')),
                FormBuilderFieldOption(
                    value: 'Test 2', child: Text('Test 2')),
                FormBuilderFieldOption(
                    value: 'Test 3', child: Text('Test 3')),
                FormBuilderFieldOption(
                    value: 'Test 4', child: Text('Test 4')),
              ],
            ),
            FormBuilderChoiceChip(
              name: 'choice_chip',
              decoration: InputDecoration(
                labelText: 'Select an option',
              ),
              options: [
                FormBuilderFieldOption(
                    value: 'Test', child: Text('Test')),
                FormBuilderFieldOption(
                    value: 'Test 1', child: Text('Test 1')),
                FormBuilderFieldOption(
                    value: 'Test 2', child: Text('Test 2')),
                FormBuilderFieldOption(
                    value: 'Test 3', child: Text('Test 3')),
                FormBuilderFieldOption(
                    value: 'Test 4', child: Text('Test 4')),
              ],
            ),
            FormBuilderDateTimePicker(
              name: 'date',
              // onChanged: _onChanged,
              inputType: InputType.time,
              decoration: InputDecoration(
                labelText: 'Appointment Time',
              ),
              initialTime: TimeOfDay(hour: 8, minute: 0),
              // initialValue: DateTime.now(),
              // enabled: true,
            ),
            FormBuilderDateRangePicker(
              name: 'date_range',
              firstDate: DateTime(1970),
              lastDate: DateTime(2030),
              format: DateFormat('yyyy-MM-dd'),
              onChanged: _onChanged,
              decoration: InputDecoration(
                labelText: 'Date Range',
                helperText: 'Helper text',
                hintText: 'Hint text',
              ),
            ),
            FormBuilderSlider(
              name: 'slider',
              validator: FormBuilderValidators.compose([
                FormBuilderValidators.min(context, 6),
              ]),
              onChanged: _onChanged,
              min: 0.0,
              max: 10.0,
              initialValue: 7.0,
              divisions: 20,
              activeColor: Colors.red,
              inactiveColor: Colors.pink[100],
              decoration: InputDecoration(
                labelText: 'Number of things',
              ),
            ),
            FormBuilderCheckbox(
              name: 'accept_terms',
              initialValue: false,
              onChanged: _onChanged,
              title: RichText(
                text: TextSpan(
                  children: [
                    TextSpan(
                      text: 'I have read and agree to the ',
                      style: TextStyle(color: Colors.black),
                    ),
                    TextSpan(
                      text: 'Terms and Conditions',
                      style: TextStyle(color: Colors.blue),
                    ),
                  ],
                ),
              ),
              validator: FormBuilderValidators.equal(
                context,
                true,
                errorText:
                    'You must accept terms and conditions to continue',
              ),
            ),
            FormBuilderTextField(
              name: 'age',
              decoration: InputDecoration(
                labelText:
                    'This value is passed along to the [Text.maxLines] attribute of the [Text] widget used to display the hint text.',
              ),
              onChanged: _onChanged,
              // valueTransformer: (text) => num.tryParse(text),
              validator: FormBuilderValidators.compose([
                FormBuilderValidators.required(context),
                FormBuilderValidators.numeric(context),
                FormBuilderValidators.max(context, 70),
              ]),
              keyboardType: TextInputType.number,
            ),
            FormBuilderDropdown(
              name: 'gender',
              decoration: InputDecoration(
                labelText: 'Gender',
              ),
              // initialValue: 'Male',
              allowClear: true,
              hint: Text('Select Gender'),
              validator: FormBuilderValidators.compose(
                  [FormBuilderValidators.required(context)]),
              items: genderOptions
                  .map((gender) => DropdownMenuItem(
                        value: gender,
                        child: Text('$gender'),
                      ))
                  .toList(),
            ),
          ],
        ),
      ),
      Row(
        children: <Widget>[
          Expanded(
            child: MaterialButton(
              color: Theme.of(context).colorScheme.secondary,
              child: Text(
                "Submit",
                style: TextStyle(color: Colors.white),
              ),
              onPressed: () {
                _formKey.currentState.save();
                if (_formKey.currentState.validate()) {
                  print(_formKey.currentState.value);
                } else {
                  print("validation failed");
                }
              },
            ),
          ),
          SizedBox(width: 20),
          Expanded(
            child: MaterialButton(
              color: Theme.of(context).colorScheme.secondary,
              child: Text(
                "Reset",
                style: TextStyle(color: Colors.white),
              ),
              onPressed: () {
                _formKey.currentState.reset();
              },
            ),
          ),
        ],
      )
    ],
  );
}

Input widgets

The currently supported fields include:

  • FormBuilderCheckbox - Single Checkbox field
  • FormBuilderCheckboxGroup - List of Checkboxes for multiple selection
  • FormBuilderChoiceChip - Creates a chip that acts like a radio button.
  • FormBuilderDateRangePicker - For selection of a range of dates
  • FormBuilderDateTimePicker - For Date, Time and DateTime input
  • FormBuilderDropdown - Used to select one value from a list as a Dropdown
  • FormBuilderFilterChip - Creates a chip that acts like a checkbox.
  • FormBuilderRadioGroup - Used to select one value from a list of Radio Widgets
  • FormBuilderRangeSlider - Used to select a range from a range of values
  • FormBuilderSegmentedControl - For selection of a value using the CupertinoSegmentedControl widget as an input
  • FormBuilderSlider - For selection of a numerical value on a slider
  • FormBuilderSwitch - On/Off switch field
  • FormBuilderTextField - A Material Design text field input.

In order to create an input field in the form, along with the label, and any applicable validation, there are several attributes that are supported by all types of inputs namely:

Attribute Type Default Required Description
name String Yes This will form the key in the form value Map
initialValue T null No The initial value of the input field
enabled bool true No Determines whether the field widget will accept user input.
decoration InputDecoration InputDecoration() No Defines the border, labels, icons, and styles used to decorate the field.
validator FormFieldValidator<T> null No A FormFieldValidator that will check the validity of value in the FormField
onChanged ValueChanged<T> null No This event function will fire immediately the the field value changes
valueTransformer ValueTransformer<T> null No Function that transforms field value before saving to form value. e.g. transform TextField value for numeric field from String to num
The rest of the attributes will be determined by the type of Widget being used.

Building your own custom field

To build your own field within a FormBuilder, we use FormBuilderField which will require that you define your own field.

var options = ["Option 1", "Option 2", "Option 3"];
FormBuilderField(
  name: "name",
  validator: FormBuilderValidators.compose([
    FormBuilderValidators.required(context),
  ]),
  builder: (FormFieldState<dynamic> field) {
    return InputDecorator(
      decoration: InputDecoration(
        labelText: "Select option",
        contentPadding:
            EdgeInsets.only(top: 10.0, bottom: 0.0),
        border: InputBorder.none,
        errorText: field.errorText,
      ),
      child: Container(
        height: 200,
        child: CupertinoPicker(
          itemExtent: 30,
          children: options.map((c) => Text(c)).toList(),
          onSelectedItemChanged: (index) {
            field.didChange(options[index]);
          },
        ),
      ),
    );
  },
),

Programmatically changing field value

You can either change the value of one field at a time like so:

_formKey.currentState.fields['color_picker'].didChange(Colors.black);

Or multiple fields value like so:

_formKey.currentState.patchValue({
  'age': '50',
  'slider': 6.7,
  'filter_chip': ['Test 1'],
  'choice_chip': 'Test 2',
  'rate': 4,
  'chips_test': [
    Contact('Andrew', '[email protected]', 'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
  ],
});

Programmatically inducing an error

Option 1 - Using FormBuilder / FieldBuilderField key

final _formKey = GlobalKey<FormBuilderState>();
final _emailFieldKey = GlobalKey<FormBuilderFieldState>();
...
FormBuilder(
  key: _formKey,
  child: Column(
    children: [
      FormBuilderTextField(
        key: _emailFieldKey
        name: 'email',
        decoration: InputDecoration(labelText: 'Email'),
        validator: FormBuilderValidators.compose([
          FormBuilderValidators.required(context),
          FormBuilderValidators.email(context),
        ]),
      ),
      RaisedButton(
        child: Text('Submit'),
        onPressed: () async {
          if(await checkIfEmailExists()){
            // Either invalidate using Form Key
            _formKey.currentState?.invalidateField(name: 'email', errorText: 'Email already taken.');
            // OR invalidate using Field Key
            _emailFieldKey.currentState?.invalidate('Email already taken.');
          }
        },
      ),
    ],
  ),
),

Option 2 - Using InputDecoration.errorText

Declare a variable to hold your error:

String _emailError;

Use the variable as the errorText within InputDecoration

FormBuilderTextField(
  name: 'email',
  decoration: InputDecoration(
    labelText: 'Email',
    errorText: _emailError,
  ),
  validator: FormBuilderValidators.compose([
      FormBuilderValidators.required(context),
      FormBuilderValidators.email(context),
  ]),
),

Set the error text

RaisedButton(
  child: Text('Submit'),
  onPressed: () async {
    setState(() => _emailError = null);
    if(await checkIfEmailExists()){
      setState(() => _emailError = 'Email already taken.');
    }
  },
),

Conditional validation

You can also validate a field based on the value of another field

FormBuilderRadioGroup(
  decoration: InputDecoration(labelText: 'My best language'),
  name: 'my_language',
  validator: FormBuilderValidators.required(context),
  options: [
    'Dart',
    'Kotlin',
    'Java',
    'Swift',
    'Objective-C',
    'Other'
  ]
    .map((lang) => FormBuilderFieldOption(value: lang))
    .toList(growable: false),
  ),
  FormBuilderTextField(
    name: 'specify',
    decoration:
        InputDecoration(labelText: 'If Other, please specify'),
    validator: (val) {
      if (_formKey.currentState.fields['my_language']?.value ==
              'Other' &&
          (val == null || val.isEmpty)) {
        return 'Kindly specify your language';
      }
      return null;
    },
  ),

Ecosystem

Here are additional packages that you can use to extend flutter_form_builder's functionality.

Support

Issues and PRs

Any kind of support in the form of reporting bugs, answering questions or PRs is always appreciated.

Coffee :-)

If this package was helpful to you in delivering your project or you just wanna to support this package, a cup of coffee would be highly appreciated ;-)

Buy me a coffee

You might also like...

Sample Flutter app for creating basic login forms validation for email and passwords

Sample Flutter app for creating basic login forms validation for email and passwords

Email validation Flutter example Sample flutter app showing how to validate a e-mail login form. This example uses the email_validator package for val

Dec 15, 2022

Pdf creation module for dart/flutter

Pdf for Dart and Flutter This set of plugins allows Flutter apps to generate and print pdf files to the device printer. This plugin works for iOS and

Jan 3, 2023

A Flutter package that provides a dropdown form field using a dropdown button inside a form field.

A Flutter package that provides a dropdown form field using a dropdown button inside a form field.

Dropdown form field A dropdown form field using a dropdown button inside a form field. Demo Features Can be used as regular form field. Simple to impl

Jan 1, 2023

Create dart data classes easily, fast and without writing boilerplate or running code generation.

Create dart data classes easily, fast and without writing boilerplate or running code generation.

Dart Data Class Generator Create dart data classes easily, fast and without writing boilerplate or running code generation. Features The generator can

Feb 28, 2022

Data Migrator - provide a universal translator for data by being portable, diverse, and efficient in migrating and converting data across discrete schemas

Data Migrator - provide a universal translator for data by being portable, diverse, and efficient in migrating and converting data across discrete schemas

Data Migrator - provide a universal translator for data by being portable, diverse, and efficient in migrating and converting data across discrete schemas

Jan 2, 2023

Flutter-Apps-Collection: a collection of apps made in flutter for learning purpose

 Flutter-Apps-Collection: a collection of apps made in flutter for learning purpose

Flutter-Apps-Collection This is a repository of a collection of apps made in flutter for learning purpose Some Screenshots . . . Apps build in Flutter

May 27, 2022

A builder for extracting a package version into code

Include the version of your package in our source code. Add build_version to pubspec.yaml. Also make sure there is a version field. name: my_pkg versi

Dec 7, 2022

Flutter form fields designed to take much of the burden of form-related coding off the programmer's back — masks, validations, keyboard type, etc.

Flutter form fields designed to take much of the burden of form-related coding off the programmer's back — masks, validations, keyboard type, etc.

well_formed Contents Overview Getting Started Demo application References Overview Well-Formed Widget Fields - Well-Formed - is a collection of Flutte

Nov 2, 2022

User auth form - Signup and signin user auth form with ability to stay signed in and have an option to signout.

user_auth_form SIgnup and signin user authentification form Getting Started This project is a starting point for a Flutter application. A few resource

Jan 6, 2022
Owner
Jannis
german student
Jannis
Responsive-Ui-builder - The responsive ui builder package contains widgets that helps you to create your UI responsive

Responsive Ui Builder Getting Started The responsive ui builder package contains

null 0 Feb 1, 2022
Simple Dart package with build-in code generation. It simplifies and speedup creation of cache mechanism for dart classes.

Simple Dart package with build-in code generation. It simplifies and speedup creation of cache mechanism for dart classes.

iteo 37 Jan 2, 2023
Flutter boilerplate - A boilerplate project created in flutter using MobX and Provider

Boilerplate Project A boilerplate project created in flutter using MobX and Prov

Wali Khan 0 Jan 22, 2022
Emanuel Braz 27 Dec 22, 2022
Create flutter project with all needed configuration in two minutes (theme, localization, connect to firebase, FCM, local notifications, safe API call, error handling, animation..etc)

Flutter GetX Template Flutter Getx template to make starting project fast and easy . Introduction We all face the same problem when we want to start a

Emad Beltaje 150 Jan 7, 2023
It is a Mobile Application built with Flutter to help Instructors reach their students with the material needed for their course (Videos, PDFs, Exams)

Droos - Flutter Mobile Appliction It is a Mobile Application built with Flutter to help Instructors reach their students with the material needed for

Abdulrahman Emad 4 Oct 5, 2022
An online learning application with all needed features implemented

ELEARN An online learning application: completely functional with payment systems and firebase added. Overview This application has been developed to

null 1 Nov 4, 2021
A Dart implementation of the cryptography needed for OMEMO 0.8.3.

omemo_dart omemo_dart is a Dart library to help developers of Dart/Flutter XMPP clients to implement OMEMO in its newest version - currently 0.8.3. Th

null 5 Nov 9, 2022
An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable.

An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable. For example the RX stream is a BehaviorSubject or a ReplaySubject.

Jon Samwell 8 Jan 22, 2022
Forms in Flutter without hassle!

Super Form Quick, familiar and extensible forms in Flutter ?? No magical configuration required ?? Managing form state with standard Flutter forms can

Bartosz Wiśniewski 11 Oct 24, 2022