Now You don’t need piling up lots of widget in Flutter and codes just to create a form. The Flutter Custom Form Builder package helps you make that possible in a very efficient and flexible way with lots of other built in widgets.
It’s a package to build Material Form with fields like TextField, DropDown, Switches, Star Rating, and many other fields etc. with the ability to create custom FormFields
and composability and reuse validation functions.
Simple Usage
To use this plugin, add custom_form_builder
as a dependency in your pubspec.yaml file.
import 'package:custom_form_builder/custom_form_builder.dart';
Example
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
Note: Avoid defining the GlobalKey inside your build method because this will create a new GlobalKey on every build cycle bringing about some erratic behavior.
Note: If you use FormBuilderDateTimePicker
import 'package:intl/intl.dart';
Full Example
import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:custom_form_builder/custom_form_builder.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: MyHomePage(title: 'FLutterFix Custom Form'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: SingleChildScrollView( child: SafeArea( child: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Column( children: <Widget>[ FormBuilder( key: _fbKey, initialValue: { 'date': DateTime.now(), 'accept_terms': false, }, autovalidate: true, child: Column( children: <Widget>[ FormBuilderDateTimePicker( attribute: "date", inputType: InputType.date, format: DateFormat("yyyy-MM-dd"), decoration: InputDecoration(labelText: "Appointment Time"), ), FormBuilderSlider( attribute: "slider", validators: [FormBuilderValidators.min(6)], min: 0.0, max: 10.0, initialValue: 1.0, divisions: 20, decoration: InputDecoration(labelText: "Number of things"), ), FormBuilderCheckbox( attribute: 'accept_terms', label: Text( "I have read and agree to the terms and conditions"), validators: [ FormBuilderValidators.requiredTrue( errorText: "You must accept terms and conditions to continue", ), ], ), FormBuilderDropdown( attribute: "gender", decoration: InputDecoration(labelText: "Gender"), // initialValue: 'Male', hint: Text('Select Gender'), validators: [FormBuilderValidators.required()], items: ['Male', 'Female', 'Other'] .map((gender) => DropdownMenuItem( value: gender, child: Text("$gender") )).toList(), ), FormBuilderTextField( attribute: "age", decoration: InputDecoration(labelText: "Age"), validators: [ FormBuilderValidators.numeric(), FormBuilderValidators.max(70), ], ), FormBuilderSegmentedControl( decoration: InputDecoration(labelText: "Movie Rating (Archer)"), attribute: "movie_rating", options: List.generate(5, (i) => i + 1) .map( (number) => FormBuilderFieldOption(value: number)) .toList(), ), FormBuilderSwitch( label: Text('I Accept the tems and conditions'), attribute: "accept_terms_switch", initialValue: true, ), FormBuilderTouchSpin( decoration: InputDecoration(labelText: "Stepper"), attribute: "stepper", initialValue: 10, step: 1, ), FormBuilderRate( decoration: InputDecoration(labelText: "Rate this form"), attribute: "rate", iconSize: 32.0, initialValue: 1.0, max: 5.0, ), FormBuilderCheckboxList( decoration: InputDecoration(labelText: "The language of my people"), attribute: "languages", initialValue: ["Dart"], options: [ FormBuilderFieldOption(value: "Dart"), FormBuilderFieldOption(value: "Kotlin"), FormBuilderFieldOption(value: "Java"), FormBuilderFieldOption(value: "Swift"), FormBuilderFieldOption(value: "Objective-C"), ], ), FormBuilderChoiceChip( attribute: "favorite_ice_cream", options: [ FormBuilderFieldOption( child: Text("Vanilla"), value: "vanilla" ), FormBuilderFieldOption( child: Text("Chocolate"), value: "chocolate" ), FormBuilderFieldOption( child: Text("Strawberry"), value: "strawberry" ), FormBuilderFieldOption( child: Text("Peach"), value: "peach" ), ], ), FormBuilderFilterChip( attribute: "pets", options: [ FormBuilderFieldOption( child: Text("Cats"), value: "cats" ), FormBuilderFieldOption( child: Text("Dogs"), value: "dogs" ), FormBuilderFieldOption( child: Text("Rodents"), value: "rodents" ), FormBuilderFieldOption( child: Text("Birds"), value: "birds" ), ], ), FormBuilderSignaturePad( decoration: InputDecoration(labelText: "Signature"), attribute: "signature", height: 100, ), ], ), ), Row( children: <Widget>[ MaterialButton( child: Text("Submit"), onPressed: () { if (_fbKey.currentState.saveAndValidate()) { print(_fbKey.currentState.value); } }, ), MaterialButton( child: Text("Reset"), onPressed: () { _fbKey.currentState.reset(); }, ), ], ) ], ) ), ), ), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }
Screenshots
Here’s the result from the code above
Input widgets
The currently supported fields include:
FormBuilderCheckbox
– Single Checkbox fieldFormBuilderCheckboxList
– List of Checkboxes for multiple selectionFormBuilderChipsInput
– Takes a list ofChip
s as input and suggests more options on typingFormBuilderChoiceChip
– Creates a chip that acts like a radio button.FormBuilderColorPicker
– ForColor
input selectionFormBuilderCountryPicker
– Country selection from listFormBuilderDateRangePicker
– For selection of a range of datesFormBuilderDateTimePicker
– ForDate
,Time
andDateTime
inputFormBuilderDropdown
– Used to select one value from a list as a DropdownFormBuilderFilterChip
– Creates a chip that acts like a checkbox.FormBuilderImagePicker
– Picker a image from Gallery or Camera and stores it in a List of images, File or String. Note: This picker is available for iOS and Android.FormBuilderPhoneField
– International phone number input.FormBuilderRadio
– Used to select one value from a list of Radio WidgetsFormBuilderRadioGroup
– Used to select one value from a list of Radio WidgetsFormBuilderRangeSlider
– Used to select a range from a range of valuesFormBuilderRate
– For selection of a numerical value as a ratingFormBuilderSegmentedControl
– For selection of a value from theCupertinoSegmentedControl
as an inputFormBuilderSignaturePad
– Presents a drawing pad on which user can doodleFormBuilderSlider
– For selection of a numerical value on a slider– Selection of a number by tapping on a plus or minus symbol. Deprecated; replaced withFormBuilderStepper
FormBuilderTouchSpin
FormBuilderSwitch
– On/Off switchFormBuilderTextField
– For text input. Accepts input of single-line text, multi-line text, password, email, urls etc by using different configurations and validatorsFormBuilderTouchSpin
– Selection of a number by tapping on a plus or minus iconFormBuilderTypeAhead
– Auto-completes user input from a list of items
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 |
---|---|---|---|---|
attribute | String | null | true | This will form the key in the form value Map |
initialValue | dynamic | null | false | The initial value of the input field |
readOnly | bool | false | false | Determines whether the field widget will accept user input. This value will be ignored if the readOnly attribute of FormBuilder widget is set to true |
decoration | InputDecoration | InputDecoration() | false | |
validators | List<FormFieldValidator> | [] | false | List of FormFieldValidator s that will check the validity of value candidate in the FormField |
onChanged | ValueChanged<T> | null | false | This event function will fire immediately the the field value changes |
valueTransformer | ValueTransformer<T> | null | false | 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 FormBuilderCustomField
which will require that you define your own FormField
. The FormField
will not require a validator
if the validators
property is already defined in the FormBuilderCustomField
.
var options = ["Option 1", "Option 2", "Option 3"];
FormBuilderCustomField( attribute: "name", validators: [ FormBuilderValidators.required(), ], formField: FormField( enabled: true, 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]); }, ), ), ); }, ), ),
Validation
The validators
attribute in fields take in any number of FormFieldValidator
allowing composability of validation functions as well as allow reusability of already defined validator methods.
Built-in Validators
The package comes with several most common FormFieldValidator
s such as required, numeric, mail, URL, min, max, minLength, maxLength, IP, credit card etc. with default errorText
in English but with ability to include you own error message that will display whenever validation fails.
Available built-in validators include:
FormBuilderValidators.required()
– requires the field have a non-empty value.FormBuilderValidators.numeric()
– requires the field’s value to be a valid number.FormBuilderValidators.min()
– requires the field’s value to be greater than or equal to the provided number.FormBuilderValidators.max()
– requires the field’s value to be less than or equal to the provided number.FormBuilderValidators.minLength()
– requires the length of the field’s value to be greater than or equal to the provided minimum length.FormBuilderValidators.maxLength()
– requires the length of the field’s value to be less than or equal to the provided maximum length.FormBuilderValidators.pattern()
– requires the field’s value to match the provided regex pattern.FormBuilderValidators.email()
– requires the field’s value to be a valid email address.FormBuilderValidators.url()
– requires the field’s value to be a valid url.FormBuilderValidators.IP()
– requires the field’s value to be a valid IP address.FormBuilderValidators.creditCard()
– requires the field’s value to be a valid credit card number.FormBuilderValidators.date()
– requires the field’s value to be a valid date string.FormBuilderValidators.requiredTrue()
– requires the field’s value be true.
Validation example:
FormBuilderTextField( attribute: "age", decoration: InputDecoration(labelText: "Age"), validators: [ FormBuilderValidators.numeric(errorText: "La edad debe ser numérica."), FormBuilderValidators.max(70), ], ),
Custom validator function
As well as the built-in validators any function of type FormFieldValidator
will be accepted into the list of validators
.
FormBuilderTextField( attribute: "over_18", decoration: InputDecoration(labelText: "Are you over 18?"), validators: [ FormBuilderValidators.required(), (val){ if(val.toLowerCase() != "yes") return "The answer must be Yes"; }, ], ),
Conditional validation
You can also validate a field based on the value of another field
FormBuilderRadio( decoration: InputDecoration(labelText: 'My best language'), attribute: "best_language", validators: [FormBuilderValidators.required()], options: [ "Dart", "Kotlin", "Java", "Swift", "Objective-C", "Other" ] .map((lang) => FormBuilderFieldOption(value: lang)) .toList(growable: false), ), FormBuilderTextField( attribute: "specify", decoration: InputDecoration(labelText: "If Other, please specify"), validators: [ (val){ if(_fbKey.currentState.fields['best_language'].currentState.value == "Other" && (val == null || val.isEmpty)) return "Kindly specify your language"; }, ], ),
Dependencies
country_pickers, date_range_picker, datetime_picker_formfield, flutter, flutter_colorpicker, flutter_touch_spin, flutter_typeahead, group_radio_button, image_picker, intl, phone_number, rating_bar, signature, validators