forme 2.5.0-alpha1
forme: ^2.5.0-alpha1 copied to clipboard
a powerful flutter form widget , for me,for you , renamed from form_builder
Screenshot #


migrate from 2.1.x to 2.5.0 #
Forme 2.5.0 has lots of changes
- support
asyncValidatoronValueField validatefromFormeValueFieldControllerwill returnFuture<String?>?instead ofString?validatefromFormeControllerwill returnFuture<Map<FormeValueFieldController>,String>instead ofMap<FormeValueFieldController,String>ValueFieldis not a subclass ofFormFieldany more- you can easily detect whether a
ValueField's value is nullable fromValueField's generic type, egValueField<String>'s value is a nonnull String ,ValueField<String?>'s value is a nullable String - remove
fieldValueListenablefromFormeFieldController - remove
lazyFieldValueListenablefromFormeKey
Simple Usage #
add dependency #
flutter pub add forme
create forme #
FormeKey key = FormeKey();// formekey is a global key , also can be used to control form
Widget child = formContent;
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 |
| onErrorChanged | false | FormeErrorChanged |
listen form field's errorText 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 |
Differences Between Form and Forme #
Forme is a form widget, but forme is not wrapped in a Form , because I don't want to refresh whole form after field's value changed or a validate performed , so it is a bit more complexable than Form.
| Difference | Form | Forme |
|---|---|---|
| AutovalidateMode | support both Form and field | only support field |
| onChanged | won't fired if value changed via state.didChange or state.setValue |
fired whenever field's value changed |
| rebuild strategy | when field value changed or perform a validation on field , all form fields will be rebuilded | only rebuild field that value changed or validated |
Forme Fields #
field type #
-StatefulField
- ValueField
- CommonField
attributes supported by all stateful fields #
| 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 |
| onFocusChanged | false | FormeFocusChanged |
listen field's focus change |
| model | true | FormeModel |
FormeModel used to provider widget render data |
| onInitialed | false | FormeFieldInitialed |
triggered when FormeFieldController initialed , when you specific a initialValue on ValueField or Forme , valueListenable will not listen this value , you can handle this value in onInitialed |
attributes supported by all value fields #
| Attribute | Required | Type | Description |
|---|---|---|---|
| onValueChanged | false | FormeValueChanged |
listen field's value change |
| onErrorChanged | false | FormeErrorChanged |
listen field's errorText change |
| validator | false | FormFieldValidator |
validate field's value |
| autovalidateMode | false | AutovalidateMode |
auto validate mode , default is AutovalidateMode.disabled |
| initialValue | false | dynamic |
initialValue,can be overwritten by forme's initialValue |
| onSaved | false | FormeFieldSetter |
triggered when call forme or field's save method |
| decoratorBuilder | false | FormeDecoratorBuilder |
used to decorate a field |
| asyncValidator | false | FormeAsyncValidator |
async validator |
| asyncValidateConfiguration | false | FormeAsyncValidateConfiguration |
async validate configuration |
currently supported fields #
| Name | Return Value | Nullable |
|---|---|---|
| FormeTextField | string | false |
| FormeDateTimeField | DateTime | true |
| FormeNumberField | num | true |
| FormeTimeField | TimeOfDay | true |
| FormeDateRangeField | DateTimeRange | true |
| FormeSlider | double | false |
| FormeRangeSlider | RangeValues | false |
| FormeFilterChip | List< T> | false |
| FormeChoiceChip | T | true |
| FormeSingleCheckbox | bool | false |
| FormeSingleSwitch | bool | false |
| FormeDropdownButton | T | true |
| FormeListTile | List< T> | false |
| FormeRadioGroup | T | true |
| FormeAsnycAutocompleteChip | List<T> | false |
| FormeAsnycAutocompleteText | T | true |
| FormeAutocompleteText | T | true |
fields from other package #
- forme_cupertino_fields cupertino style fields
Forme Model #
you can update a widget easily with FormeModel
eg: if you want to update labelText of a FormeTextField, you can do this :
FormeFieldController controller = formKey.field(fieldName);
controller.update(FormeTextFieldModel(decoration:InputDecoration(labelText:'New Label')));
if you want to update items of FormeDropdownButton:
controller.updateModel(FormeDropdownButtonModel<String>(
icon: SizedBox(
width: 14,
height: 14,
child: CircularProgressIndicator(),
)));
Future<List<DropdownMenuItem<String>>> future =
Future.delayed(Duration(seconds: 2), () {
return FormeUtils.toDropdownMenuItems(
['java', 'dart', 'c#', 'python', 'flutter']);
});
future.then((value) {
controller.updateModel(FormeDropdownButtonModel<String>(
icon: Icon(Icons.arrow_drop_down), items: value));
});
update model will auto copywith old model's attribute
Custom way display error text #
if default error text display can not fit your needs , you can implementing a custom error display via ValueField's onErrorChanged or FormeValueFieldController's errorTextListenable
don't forget to set Forme's quieltyValidate attribute to true
via onErrorChanged #
onErrorChanged will triggered whenever errorText of field changes , it is suitable when you want to update a stateful field according to error state of field
eg: change border color when error state changes
FormeTextField(
validator: validator,
onErrorChanged: (m, a) {
InputBorder border = OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: BorderSide(color: a == null ? Colors.green : Colors.red, width: 1));
m.updateModel(FormeTextFieldModel(
decoration: InputDecoration(
focusedBorder: border, enabledBorder: border)));
},
),
via errorTextListenable #
errorTextListenable is more convenient than onErrorChanged sometimes.
eg: when your want to display an valid or invalid suffix icon according to error state of field, in onErrorChanged , update model will rebuild whole field,
but with errorTextListenable, you can only rebuild the suffix icon, below is an example to do this:
suffixicon: Builder(
builder: (context) {
FormeValueFieldController<String, FormeModel>
controller = FormeFieldController.of(context);
return ValueListenableBuilder<FormeValidateError?>(
valueListenable: controller.errorTextListenable,
child: const IconButton(
onPressed: null,
icon: const Icon(
Icons.check,
color: Colors.green,
)),
builder: (context, errorText, child) {
if (errorText == null)
return SizedBox();
else
return errorText.isPresent
? const IconButton(
onPressed: null,
icon: const Icon(
Icons.error,
color: Colors.red,
))
: child!;
});
},
),
listenable #
there are five listenables in field
- focusListenable
- readOnlyListenable
- errorTextListenable
- valueListenable
- modelListenable
get listenable #
you can get focusListenable and readOnlyListenable from FormeFieldController,
get valueListenable and errorTextListenable from FormeValueFieldController
FormeTextField(
decoratorBuilder:FormeDecoratorBuilder(),//decorator is a part of field
mode:FormeTextFieldModel(
decoration:InputDecoration(
suffixIcon: Builder(builder: (context) {
FormeFieldController controller = FormeFieldController.of(context);
return ValueListenable<bool>(valueListenable:controller. valueListenable,builder:(context,focus,child){
if(focus) return Icon(Icons.clear);
return SizedBox();
})
}),
),
),
)
validate #
sync validate #
sync validate is supported by FormeValidates
| 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
async validated #
async validator is supported after Forme 2.2.0 , you can specific an asyncValidator on ValueField , the unique difference
between validator and asyncValidator is asyncValidator return a Future and validator return a String
when perform a asyncValidator
if FormField.autovalidateMode is AutovalidateMode.disabled , asyncValidator will never be performed unless you call validate from FormeValueFieldController manually.
if you specific both validator and asyncValidator , asyncValidator will only be performed after validator passed.
if you specific an asyncValidateConfiguration on ValueField and names attribute is not empty , when field's (which name is in names) value changed , asyncValidator will be performed if last validation is validated by asyncValidator.
debounce
you can specific a debounce on asyncValidateConfiguration , debounce will not worked when you manually call validate on FormeValueFieldController
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 value field's controller #
T controller = formeKey.valueField<T extends FormeValueFieldController>(String name);
get form data #
Map<String, dynamic> data = formeKey.data;
validate #
since 2.2.0 , this method will return a Future ranther than a Map
Future<Map<FormeValueFieldController,String>> errorsFuture = formKey.validate({bool quietly = false});
set form data #
formeKey.data = Map<String,dynamic> data;
reset form #
formeKey.reset();
save form #
formeKey.save();
whether validate is quietly #
bool quietlyValidate = formKey.quietlyValidate;
set quietlyValidate #
formeKey.quieltyValidate = bool quietlyValidate;
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 focused #
bool hasFocus = field.hasFocus;
focus|unfocus current Field #
field.requestFocus();
field.unfocus();
focus next #
field.nextFocus();
set field model #
field.model = FormeModel model;
update field model #
field.updateModel(FormeModel model);
get field model #
FormeModel model = field.model;
ensure field is visible in viewport #
Future<void> result = field.ensureVisibe({Duration? duration,
Curve? curve,
ScrollPositionAlignmentPolicy? alignmentPolicy,
double? alignment});
get focusListenable #
ValueListenable<bool> focusListenable = field.focusListenable;
get readOnlyListenable #
ValueListenable<bool> readOnlyListenable = field.readOnlyListenable;
get modelListenable #
ValueListenable<FormeModel> modelListenable = field.modelListenable;
Forme Value Field Methods #
FormeValueFieldController is extended FormeFieldController
get field value #
dynamic value = valueField.value;
set field value #
valueField.value = dynamic data;
reset field #
valueField.reset();
validate field #
since 2.2.0 , this method will return a Future ranther than a String
Future<String?>? errorFuture = valueField.validate({bool quietly = false});
get error #
FormeValidateError? error = valueField.error;
get decorator controller #
FormeDecoratorController decoratorController = valueField.decoratorController;
get errorTextListenable #
ValueListenable<FormeValidateError?> errorTextListenable = valueField.errorTextListenable;
get valueListenable #
ValueListenable<dynamic> valueListenable = valueField.valueListenable;
get oldValue #
dynamic value = valueField.oldValue;
build your field #
- create your
FormeModel, if you don't need it , useFormeEmptyModelinstead - create your
ValueField<T,E>, T is your field return value's type, E is yourFormeModel's type - if you want to create your custom
State,extendsValueFieldState<T,E>
links below is some examples to help you to build your field
common field #
- https://github.com/wwwqyhme/forme/blob/main/lib/src/field/forme_visible.dart
- https://github.com/wwwqyhme/forme/blob/main/lib/src/field/forme_flex.dart