Flutter form builder

  Form

Intro: A powerful flutter form widget, easy to use and extend. provide rich API to simplify form control and sync|async validation.

Web Demo

https://www.qyh.me/forme3/

Simple Usage

add dependency

flutter pub add forme
/// add base fields
flutter pub add forme_base_fields 

create forme

FormeKey key = FormeKey();// formekey is a global key , also  used to control form
Widget child = FormeTextField(name:'username',decoration:const InputDecoration(labelText:'Username'));
Widget forme = Forme(
	key:key,
	child:child,
)

Forme Attributes

AttributeRequiredTypeDescription
keyfalseFormeKeya global key, also used to control form
childtrueWidgetform content widget
readOnlyfalseboolwhether form should be readOnly,default is false
onValueChangedfalseFormeValueChangedlisten form field’s value change
initialValuefalseMap<String,dynamic>initialValue , will override FormField’s initialValue
onFieldValidationChangedfalseFormeFieldValidationChangedlisten form validation changed
onValidationChangedfalseFormeValidationChangedlisten form validation change
onWillPopfalseWillPopCallbackSignature for a callback that verifies that it’s OK to call Navigator.pop
quietlyValidatefalseboolif this attribute is true , will not display default error text
onFocusChangedfalseFormeFocusChangedlisten form field’s focus change
autovalidateModefalseAutovalidateModeauto validate form mode
autovalidateByOrderfalseboolwhether auto validate form by order
onFieldsChangedfalsefunctionlisten every field initialled or disposed

FormeField

attributes supported by all FormeField

AttributeRequiredTypeDescription
nametrueStringfield’s id,should be unique in form
buildertrueFieldContentBuilderbuild field content
readOnlyfalseboolwhether field should be readOnly,default is false
enabledfalseboolwhether field is enabled , default is true
quietlyValidatetrueboolwhether validate quietly
asyncValidatorDebouncefalseDurationasync validate debounce , default is 500ms
autovalidateModefalseAutovalidateModeautovalidate mode
onValueChangedfalseFormeValueChangedtriggered when field’s value changed
onFocusChangedfalseFormeFocusChangedtriggered when field’s focus state changed
onValidationChangedfalseFormeFieldValidationChangedtriggered when field’s validation error changed
onInitialedfalseFormeFieldInitialedtriggered when field initialed
onSavedfalseFormeFieldSettertriggered when form saved
validatorfalseFormeValidatorsync validator
asyncValidatorfalseFormeAsyncValidatorasync validator
decoratorfalseFormeFieldDecoratorused to decorator a field
orderfalseintorder of field
requestFocusOnUserInteractionfalseboolwhether request focus when field value changed by user interaction
registrablefalseboolwhether this field should be registered to Forme

async validate

async validator is supported after Forme 2.5.0 , you can specific an asyncValidator on FormeField , the unique difference between validator and asyncValidator is asyncValidator return a Future<String> and validator return a String

when to perform an asyncValidator

if FormeField.autovalidateMode is AutovalidateMode.disabled , asyncValidator will never be performed unless you call validate from FormeFieldController manually.

if you specific both validator and asyncValidator , asyncValidator will only be performed after validator return null.

after successful performed an asyncValidator , asyncValidator will not performed any more until field’s value changed

debounce

you can specific a debounce on FormeField , debounce will not worked when you manually call validate on FormeFieldController

whether validation itself is valid

in some cases,when an async validation is performing , another validation on same field is performed,in this case ,previous validation is invalid , so if you want to update UI before return validation result in async validator , you need to validate it first,eg:

asyncValidator:(field,value,isValid){
    return Future.delayed(const Duration(seconds:2),(){
        if(isUnexceptedValue(value)) {
            if(isValid()){
                updateUI();
            }
            return 'invalid';
        }
        return null;
    });    
}

validates

you can use FormeValidates to simplify your validators

Validator NameSupport TypeWhen ValidWhen Invalid
notNulldynamicvalue is not nullvalue is null
sizeIterable Map String1. value is null 2. max & min is null 3. String’s length or Collection’s size is in [min,max]String’s length or Collection’s size is not in [min,max]
minnum1. value is null 2. value is bigger than minvalue is smaller than min
maxnum1. value is null 2. value is smaller than maxvalue is bigger than max
notEmptyIterable Map String1. value is not null 2. String’s length or Collection’s size is bigger than zero1. value is null 2. String’s length or Collection’s size is zero
notBlankString1. value is null 2. value.trim()’s length is not nullvalue’length is zero after trimed
positivenum1. value is null 2. value is bigger than zerovalue is smaller than or equals zero
positiveOrZeronum1. value is null 2. value is bigger than or equals zerovalue is smaller than zero
negativenum1. value null 2. value is smaller than zerovalue is bigger than or equals zero
negativeOrZeronum1. value null 2. value is smaller than or equals zerovalue is bigger than zero
patternString1. value null 2. value matches patternvalue does not matches pattern
emailString1. value null 2. value is a valid emailvalue is not a valid email
urlString1. value is null 2. value is empty or value is a valid urlvalue is not a valid url
rangenum1. value null 2. value is in rangevalue is out of range
equalsdynamic1. value null 2. value is equals target valuevalue is not equals target value
anyTany validators is validevery validators is invalid
allTall validators is validany validators is invalid

when you use validators from FormeValidates , you must specific at least one errorText , otherwise errorText is an empty string

FormeKey Methods

whether form has a name field

bool hasField = formeKey.hasField(String name);

whether current form is readOnly

bool readOnly = formeKey.readOnly;

set readOnly

formeKey.readOnly = bool readOnly;

get field’s controller

T controller = formeKey.field<T extends FormeFieldController>(String name);

get form data

Map<String, dynamic> data = formeKey.data;

set form data

formeKey.data = Map<String,dynamic> data;

validate

since 2.5.0 , this method will return a Future ranther than a Map

you can use FormeValidateSnapshot.isValueChanged to check whether form value is changed duration this validation , if is changed , typically means this validation is invalid , you should not submit your form even though validation is passed

Future<FormeValidateSnapshot> future = formKey.validate({
    bool quietly = false,
    Set<String> names = const {},
    bool clearError = false,
    bool validateByOrder = false,
});

get validation

FormeValidation validation = formKey.validation;

reset form

formeKey.reset();

save form

formeKey.save();

whether validate is quietly

bool quietlyValidate = formKey.quietlyValidate;

set quietlyValidate

formeKey.quieltyValidate = bool quietlyValidate;

is value changed after initialed

bool isChanged = formeKey.isValueChanged

get all field controllers (2.5.2)

List<FormeFieldController> controllers = formeKey.controllers;

get fieldListenable

ValueListenable<FormeFieldController> fieldListenable = formeKey.fieldListenable(String name);

get fieldsListenable

ValueListenable<Map<String, FormeFieldController<dynamic>?>> fieldsListenable = 
      formeKey.fieldsListenable;

used to listen every field initialed or disposed of, value is an empty or single sized map , key is the field name, value is FormeFieldController

get validation listenable

ValueListenable<FormeValidation> validationListenable = formeKey.validationListenable;

useful when you want to show or hide a submit button when validation passed or not,eg:

 Builder(
    builder: (context) {
    return ValueListenableBuilder<FormeValidation>(
        valueListenable: key.validationListenable,
        builder: (context, validation, child) {
            if (!validation.isValidOrUnnecessaryOrEmpty) {
                return const SizedBox.shrink();
            }
            return yourSubmitButton;
        });
    },
),

Forme Field Methods

get forme controller

FormeController? formeController = field.formeController;

get field’s name

String name = field.name

whether current field is readOnly

bool readOnly = field.readOnly;

set readOnly on field

field.readOnly = bool readOnly;

whether current field is enabled

bool enabled = field.enabled;

set enabled on field

field.enabled = bool enabled;

get focusNode

FocusNode? focusNode = field.focusNode;

get context

BuilderContext context = field.context;

get focusListenable

ValueListenable<bool> focusListenable = field.focusListenable;

get readOnlyListenable

ValueListenable<bool> readOnlyListenable = field.readOnlyListenable;

get enabledListenable

ValueListenable<bool> enabledListenable = field.enabledListenable;

get value

T value = field.value;

set value

field.value = T data;

reset field

field.reset();

validate field

since 2.5.0 , this method will return a Future ranther than a String

Future<FormeFieldValidateSnapshot> future = field.validate({bool quietly = false});

get validation

FormeFieldValidation validation = field.validation;

get validationListenable

ValueListenable<FormeFieldValidation>  validationListenable = field.validationListenable;

get valueListenable

ValueListenable<T> valueListenable = field.valueListenable;

get oldValue

if the value changed , you can use this method to get the previous value

T? value = field.oldValue;

is value changed

bool isChanged = field.isValueChanged

whether is field mounted

bool mounted = field.mounted

get generic type

Type type = field.type;

whether field value is nullable

bool isNullable = field.isNullable;

FocusNode

To simplify form control, Forme does not support set focus node on the field, FocusNode will be auto-created when needed.

custom focus node

if you want to override default focusNode , you can extends FormeFieldState and use set focusNode method to do that, in this case, you must dispose of focusNode by yourself

custom field

FormeField<String>(
    name: 'customField',
    initialValue: 'currentValue',
    builder: (FormeFieldState<String> state) {
        return TextButton(
        onPressed: () {
            state.didChange('newValue');
        },
        child: Text(state.value),
        );
    },
),

Download and contribute to this plugin source code on GitHub

flutter form builder
https://github.com/wwwqyhme/forme
2 forks.
0 stars.
3 open issues.

Recent commits: