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

Attribute Required Type Description
key false FormeKey a global key, also used to control form
child true Widget form content widget
readOnly false bool whether form should be readOnly,default is false
onValueChanged false FormeValueChanged listen form field's value change
initialValue false Map<String,dynamic> initialValue , will override FormField's initialValue
onFieldValidationChanged false FormeFieldValidationChanged listen form validation changed
onValidationChanged false FormeValidationChanged listen form validation change
onWillPop false WillPopCallback Signature for a callback that verifies that it's OK to call Navigator.pop
quietlyValidate false bool if this attribute is true , will not display default error text
onFocusChanged false FormeFocusChanged listen form field's focus change
autovalidateMode false AutovalidateMode auto validate form mode
autovalidateByOrder false bool whether auto validate form by order
onFieldsChanged false function listen every field initialled or disposed

FormeField

attributes supported by all FormeField

Attribute Required Type Description
name true String field's id,should be unique in form
builder true FieldContentBuilder build field content
readOnly false bool whether field should be readOnly,default is false
enabled false bool whether field is enabled , default is true
quietlyValidate true bool whether validate quietly
asyncValidatorDebounce false Duration async validate debounce , default is 500ms
autovalidateMode false AutovalidateMode autovalidate mode
onValueChanged false FormeValueChanged triggered when field's value changed
onFocusChanged false FormeFocusChanged triggered when field's focus state changed
onValidationChanged false FormeFieldValidationChanged triggered when field's validation error changed
onInitialed false FormeFieldInitialed triggered when field initialed
onSaved false FormeFieldSetter triggered when form saved
validator false FormeValidator sync validator
asyncValidator false FormeAsyncValidator async validator
decorator false FormeFieldDecorator used to decorator a field
order false int order of field
requestFocusOnUserInteraction false bool whether request focus when field value changed by user interaction
registrable false bool whether this field should be registered to Forme

async validate

https://raw.githubusercontent.com/wwwqyhme/wwwqyhme.github.io/main/forme3/async_validation.gif

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 Name Support Type When Valid When Invalid
notNull dynamic value is not null value is null
size Iterable Map String 1. 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
min num 1. value is null 2. value is bigger than min value is smaller than min
max num 1. value is null 2. value is smaller than max value is bigger than max
notEmpty Iterable Map String 1. value is not null 2. String's length or Collection's size is bigger than zero 1. value is null 2. String's length or Collection's size is zero
notBlank String 1. value is null 2. value.trim()'s length is not null value'length is zero after trimed
positive num 1. value is null 2. value is bigger than zero value is smaller than or equals zero
positiveOrZero num 1. value is null 2. value is bigger than or equals zero value is smaller than zero
negative num 1. value null 2. value is smaller than zero value is bigger than or equals zero
negativeOrZero num 1. value null 2. value is smaller than or equals zero value is bigger than zero
pattern String 1. value null 2. value matches pattern value does not matches pattern
email String 1. value null 2. value is a valid email value is not a valid email
url String 1. value is null 2. value is empty or value is a valid url value is not a valid url
range num 1. value null 2. value is in range value is out of range
equals dynamic 1. value null 2. value is equals target value value is not equals target value
any T any validators is valid every validators is invalid
all T all validators is valid any 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 , value is an empty or single sized map , key is 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 value changed , you can use this method to get 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

for simplify form control , Forme not support set focus node on 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 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),
        );
    },
),

Libraries

forme