forme 2.5.3 forme: ^2.5.3 copied to clipboard
a powerful flutter form widget ,easy to use and extend. provide rich api to simplify form control and sync|async validation
demo(web) #
material_fields
https://wwwqyhme.github.io/forme/
cupertino_fields
https://wwwqyhme.github.io/forme_cupertino/
Screenshot #
migrate from 2.1.x to 2.5.0 #
- remove
onValueChanged
,onErrorChanged
,onFocusChanged
,onInitialed
,validator
,autovalidateMode
onField
, they are moved toFormeFieldListener
,validator
is renamed toonValidate
- support
onAsyncValidate
andasyncValidatorDebounce
onFormeValueFieldListener
to support async validate validator
is renamed toonValidate
onValidate
accept two params , the first is current controller , second is current value- remove
fieldListenable
fromFormeFieldController
- remove
lazyFieldListenable
fromFormeKey
ValueField
is not aFormField
any more- you can create a nonnull or nullable
ValueField
byValueField
's generic type , eg:ValueField<String>
is nonnull , butValueField<String?>
is nullable - remove
clearValue
fromFormeValueFieldController
- support
comparator
onValueField
, which used to compare values before update field's value - support
autovalidateMode
onForme
Simple Usage #
add dependency #
flutter pub add forme
create forme #
FormeKey key = FormeKey();// formekey is a global key , also 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 |
autovalidateMode | false | AutovalidateMode |
auto validate form mode |
autovalidateByOrder | false | bool |
whether auto validate form by order |
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 |
model | true | FormeModel |
FormeModel used to provide widget render data |
listener | false | FormeFieldListener |
user to listen onInitialed ,onFocusChanged |
attributes supported by all value fields #
Attribute | Required | Type | Description |
---|---|---|---|
decoratorBuilder | false | FormeDecoratorBuilder |
used to decorate a field |
listener | false | FormeValueFieldListenable |
user to listen onValueChanged ,onErrorChanged ,onValidate ,onAsyncValidate ,onSaved |
order | false | int | field order |
quietlyValidate | false | bool |
if this attribute is true , will not display default error text |
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 packages #
- 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.5.0 , you can specific an onAsyncValidate
on ValueField
's listener , the unique difference
between onValidate
and onAsyncValidate
is onAsyncValidate
return a Future<String>
and onValidate
return a String
when to perform a asyncValidator
if ValueField.autovalidateMode
is AutovalidateMode.disabled
, asyncValidator will never be performed unless you call validate
from FormeValueFieldController
manually.
if you specific both onValidate
and onAsyncValidate
, onAsyncValidate
will only be performed after onValidate
return null.
after successful performed an async validator , async validator will not performed any more until field's value changed
debounce
you can specific a debounce on FormeValueFieldListener
, 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.5.0 , this method will return a Future ranther than a Map
Future<FormeValidateSnapshot> future = formKey.validate({bool quietly = false,Set<String> names = const {},
bool clearError = false,
bool validateByOrder = 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;
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);
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.5.0 , this method will return a Future ranther than a String
Future<FormeFieldValidateSnapshot> future = 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;
is value changed #
bool isChanged = valueField.isValueChanged
build your field #
- create your
FormeModel
, if you don't need it , useFormeEmptyModel
instead - 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