flutter_form_builder

basic usage

FormManagement formManagement = FormManagement();

Widget form = FormBuilder(
      {bool readOnly,//set form's readonly state
      bool visible, // set form's visible state
      FormThemeData? formThemeData, //set themedata of form 
      this.formManagement,
      bool enableLayoutManagement// when you really want to use FormLayoutManagement,you should set this property to true,otherwise set to false for performance improve})
	.textField(
          controlKey: 'username',
          labelText: 'username',
          clearable: true,
          validator: (value) =>
              value.isEmpty ? 'username can not be empty !' : null,
        )
        .switchInline(
          controlKey: 'switch1',
          onChanged: (value) => print('switch1 value changed $value'),
        )
        .nextLine()
        .textField(
            controlKey: 'password',
            hintText: 'password',
            obscureText: true,
            passwordVisible: true,
            clearable: true,
            toolbarOptions: ToolbarOptions(copy: false, paste: false),
            onChanged: (value) => print('password value changed $value'),
            flex: 1)
        .textButton(
            onPressed: () {
              formManagement
                  .getFormFieldManagement('password')
                  .textSelectionManagement
                  .selectAll();
            },
            label: 'button',
            controlKey: 'button')
        .nextLine()
        .numberField(
          controlKey: 'age',
          hintText: 'age',
          clearable: true,
          flex: 3,
          min: -18,
          max: 99,
          decimal: 0,
          onChanged: (value) => print('age value changed $value'),
          validator: (value) => value == null ? 'not empty' : null,
        )
        .checkboxGroup(
          items: FormBuilder.toCheckboxGroupItems(['male', 'female']),
          controlKey: 'checkbox',
          split: 2,
          label: 'sex',
          onChanged: (value) => print('checkbox value changed $value'),
          validator: (value) => value.isEmpty ? 'pls select sex' : null,
        );

form layout

Column Row Flexible(FlexFit.tight) Padding FormField

methods

FormManagement

get FormThemeData

FormThemeData formThemeData => formManagement.formThemeData

set FormThemeData

formManagement.formThemeData = FormThemeData(themeData);// system theme
formManagement.formThemeData = DefaultFormTheme(); //default theme from  https://github.com/mitesh77/Best-Flutter-UI-Templates/blob/master/best_flutter_ui_templates/lib/hotel_booking/filters_screen.dart

has controlKey

bool hasControlKey => formManagement.hasControlKey(String controlKey);

whether form is visible

bool visible = formManagement.visible;

hide|show form

formManagement.visible = true|false;

whether form is readOnly

bool readOnly = formManagement.readOnly;

set form readonly|editable

formManagement.readOnly = true|false;

get rows of form

int rows = formManagement.rows;

whether enableLayoutManagement

bool enableLayoutManagement = formManagement.enableLayoutManagement;

create FormFieldManagement

if your field does not has a controlKey use newFormFieldManagementByPosition instead

FormFieldManagement formFieldManagement =  newFormFieldManagement(String controlKey);

create FormFieldManagement by position

if field at position has a controlKey, you should use newFormFieldManagement rather than this method!

FormFieldManagement formFieldManagement =  newFormFieldManagementByPosition(int row,int column);

get form data

only contains field which has a controlKey

Map<String,dynamic> dataMap = formManagement.data;

reset form

formManagement.reset();

validate form

formManagement.validate();

whether form is valid

unlike validate method, this method won't display error msg

formManagement.isValid

get error

Map<FieldKey, String> errorMap = formManagement.error;

get focusable & invalid fields

has been sorted by row & column

Iterable<FocusableInvalidField> fields = focusManagement.getFocusableInvalidFields();

create FormRowManagement

FormRowManagement formRowManagement = formManagement.newFormRowManagement(int row);

create FormLayoutManagement

FormLayoutManagement formLayoutManagement = newFormLayoutManagement()

FormFieldManagement

whether field is focusable

bool focusable => formFieldManagement.focusable

whether field is focused

bool focused => formFieldManagement.hasFocus

focus|unfocus field

formFieldManagement.focus = true|false

set focuslistener

call this method in WidgetsBinding.instance!.addPostFrameCallback

formFieldManagement.focusListener = (key,hasFocus){
  // when key is null, root node focused
  // otherwise sub node focused
}

get ValueFieldManagement

field must be a valuefield ,otherwise an error will be throw

ValueFieldManagement ValueFieldManagement  = formFieldManagement.valueFieldManagement 

whether field is ValueField

bool isValueField = formFieldManagement.isValueField;

whether field support textSelection

bool supportTextSelection = formFieldManagement.supportTextSelection

get TextSelectionManagement

if field don't support textSelection ,an error will be throw

TextSelectionManagement  textSelectionManagement = formFieldManagement.textSelectionManagement

whether field is readOnly

bool readOnly = formFieldManagement.readOnly;

set readOnly

formFieldManagement.readOnly = true|false

whether field is visible

bool visible = formFieldManagement.visible;

set visible

formFieldManagement.visible = true|false

get padding

EdgeInsets? padding = formFieldManagement.padding;

set padding

formFieldManagement.padding = padding;

rebuild field's state

see supported states for every field here

formFieldManagement.state = {};

update field's state

see supported states for every field here

formFieldManagement.update({});

update one field state

see supported states for every field here

formFieldManagement.update1(String key,dynamic value);

remove field's states

see supported states for every field here

formFieldManagement.removeStateKey(Set<String> keys); 

ValueFieldManagement

get value

dynamic value => valueFieldManagement.value

set value

valueFieldManagement.value = value;//will trigger onChanged
valueFieldManagement.setValue(dynamic value,{bool trigger})

whether field is valid

this method won't display error msg

bool isValid = valueFieldManagement.isValid;

validate field

this method will display error and return whether field is valid

bool isValid = valueFieldManagement.validate();

reset field

valueFieldManagement.reset();

get error

String? error = valueFieldManagement.error;

FormRowManagement

get columns

int columns = formRowManagement.rows;

whether row is visible

bool visible = formRowManagement.visible;

set visible

formRowManagement.visible = true|false;

set readOnly

formManagement.readOnly = true|false;

FormLayoutManagement

whether a layout is editing

bool isEditing = formLayoutManagement.isEditing;

get rows of currrent editing layout

int rows = formLayoutManagement.rows;

get columns of a row in current editing layout

int columns = formLayoutManagement.getColumns(int row);

remove a row|field in layout

formLayoutManagement.remove(int row,{int column});

insert field at position

void insert(
      {int? column,
      int? row,
      String? controlKey,
      int? flex,
      bool visible = true,
      EdgeInsets? padding,
      required AbstractFormField field,
      bool inline = true,
      bool insertRow = false})

swap two rows

formLayoutManagement.swapRow(int oldRow, int newRow)

start edit current layout

formLayoutManagement.startEdit();

apply edited layout

formLayoutManagement.apply();

cancel editing layout

formLayoutManagement.cancel();

field states

ClearableTextFormField

name Type nullable
labelText String true
hintText String true
keyboardType TextInputType true
autofocus bool false
maxLines int true
maxLength int true
clearable bool false
prefixIcon Widget true
inputFormatters List< TextInputFormatter> true
style TextStyle true
toolbarOptions ToolbarOptions true
selectAllOnFocus bool false
suffixIcons List< Widget> true
textInputAction TextInputAction true
inputDecorationTheme InputDecorationTheme true

DateTimeFormField

name Type nullable
labelText String true
hintText String true
maxLines int false
useTime bool false
formatter DateTimeFormatter true
style TextStyle true
inputDecorationTheme InputDecorationTheme true

NumberFormField

name Type nullable
labelText String true
hintText String true
autofocus bool false
clearable bool false
prefixIcon Widget true
style TextStyle true
suffixIcons List< Widget> true
textInputAction TextInputAction true
inputDecorationTheme InputDecorationTheme true
decimal int false
max max true
min min true

CheckboxGroupFormField

name Type nullable
label String true
split int false
items List< CheckboxGroupItem> false
errorTextPadding EdgeInsets false

RadioGroupFormField

name Type nullable
label String true
split int false
items List< RadioGroupItem> false
errorTextPadding EdgeInsets false

SelectorFormField

name Type nullable
labelText String true
hintText String true
multi bool false
clearable bool false
inputDecorationTheme InputDecorationTheme true

SliderFormField

name Type nullable
label String true
max double false
min double false
divisions int false
contentPadding EdgeInsets false

RangeSliderFormField

name Type nullable
label String true
max double false
min double false
divisions int false
contentPadding EdgeInsets false

SwitchGroupFormField

name Type nullable
label String true
items List< SwitchGroupItem> false
hasSelectAllSwitch bool false
selectAllPadding EdgeInsets false
errorTextPadding EdgeInsets false

FilterChipFormField

name Type nullable
label String true
items List< FilterChipItem<T>> false
pressElevation double true
errorTextPadding EdgeInsets false

currently support fields

field return value nullable
ClearableTextFormField string false
CheckboxGroupFormField List< int> false
RadioGroupFormField T true
DateTimeFormField DateTime true
SelectorFormField List< T> false
SwitchGroupFormField List< int> false
SwitchInlineFormField bool false
NumberFormField num true
SliderFormField double false
RangeSliderFormField RangeValues false
FilterChipFormField List< T> false

build your own form field

build a value field

if you want to build a nullable value field ,just extends ValueField

this is an example:

class CustomNullableValueField<T> extends ValueField<T> {
  CustomNullableValueField({
    required T value,
    String? label,
    TextStyle? textStyle,
    T? initialValue,
    ValueChanged<T?>? onChanged,
    FormFieldValidator<T>? validator,
  }) : super(
          {
            'label': TypedValue<String?>(label),
            'textStyle': TypedValue<TextStyle?>(textStyle),
          }, //this is a initStateMap
          initialValue: initialValue,
          validator: validator,
          onChanged: onChanged,
          builder: (state, stateMap, readOnly, formThemeData) {
            // state is ValueFieldState<T>,if you override ValueFieldState,you can cast it as your custom ValueFieldState,in this example you can cast it to _CustomNullableValueFieldState,you can get controlKey via state.controlKey(nullable),you can also get row|column|flex|inline to help you building your widget
            // readOnly : this field should readOnly
            // stateMap : can be regarded as the lastest initStateMap , user can change stateMap var FormFieldManagement's update|removeState|directly set state, in this example ,you should get label&textStyle form stateMap rather than directly use them
            // formThemeData : FormThemeData
            return Row(
              children: [
                Text(
                  stateMap['label'] ??
                      'radio', //don't use just label here,you should get lastest label from stateMap,the key is initStateMap's label key
                  style: stateMap['textStye'],
                ),
                Radio<T>(
                  focusNode: state
                      .focusNode, //state has prepared a focusnode for you,just use it rather than create a new one
                  activeColor: formThemeData.themeData.primaryColor,
                  groupValue: state.value,
                  value: value,
                  onChanged:
                      readOnly ? null : (value) => state.didChange(value),
                )
              ],
            );
          },
        );

  @override
  _CustomNullableValueFieldState<T> createState() =>
      _CustomNullableValueFieldState();
}

if you also want to build custom ValueFieldState,extends it !

class _CustomNullableValueFieldState<T> extends ValueFieldState<T>
    with TextSelectionManagement // if you form field support textselection
{
  late final TextEditingController
      textEditingController; //just an example here !

  @override
  void initState() {
    super.initState();
    textEditingController = TextEditingController(
        text:
            widget.initialValue == null ? '' : widget.initialValue.toString());
  }

  @override
  void reset() {
    super.reset();
    textEditingController.text =
        widget.initialValue == null ? '' : widget.initialValue.toString();
  }

  @override
  void dispose() {
    textEditingController.dispose();
    super.dispose();
  }

  @override
  void selectAll() {
    setSelection(0, textEditingController.text.length);
  }

  @override
  void setSelection(int start, int end) {
    TextSelectionManagement.setSelectionWithTextEditingController(
        start, end, textEditingController);
  }
}

the last step is insert your field

.field(
    controlKey:'test',//important !,used to maintain state and used as a key when you get data via FormManagement.data
    field: CustomNullableValueField<String>(
        onChanged: (value) => print(value), value: '123'),
    flex: 1,
    inline: true),//if inline is false,the field will hole whole column,otherwise flex will work

build a nonnull value field is almost the same,just extends NonnullValueField

this is an example:

class CustomNonnullableValueField extends NonnullValueField<bool> {
  CustomNonnullableValueField({
    String? label,
    TextStyle? textStyle,
    required bool initialValue,
    ValueChanged<bool>? onChanged,
    NonnullFieldValidator<bool>? validator,
  }) : super(
          {
            'label': TypedValue<String?>(label),
            'textStyle': TypedValue<TextStyle?>(textStyle),
          },
          initialValue: initialValue,
          validator: validator,
          onChanged: onChanged,
          builder: (state, stateMap, readOnly, formThemeData) {
            return Row(
              children: [
                Text(
                  stateMap['label'] ?? 'checkbox',
                  style: stateMap['textStye'],
                ),
                Checkbox(
                  activeColor: formThemeData.themeData.primaryColor,
                  value: state.value,
                  onChanged: (value) => state.didChange(value),
                )
              ],
            );
          },
        );
}

build a commonfield

class Label extends CommonField {
  final String label;
  Label(this.label)
      : super(
          {'label': TypedValue<String>(label)},
          builder: (state, stateMap, readOnly, formThemeData) {
            return Text(
              stateMap['label'],
              style: TextStyle(
                  fontSize: 18, color: formThemeData.themeData.primaryColor),
            );
          },
        );
}

build a StatelessField

StatelessField field = StatelessField(builder: (context) {
    BuilderInfo info = BuilderInfo.of(context);
    FieldKey fieldKey = info.fieldKey;
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          'I\'m a custom field',
          style: TextStyle(fontSize: 20),
        ),
        Container(
          color: info.formThemeData.themeData.primaryColor
              .withOpacity(0.3),
          child: ListView(
            shrinkWrap: true,
            children: [
              createRow('row', '${fieldKey.row}'),
              createRow('column', '${fieldKey.column}'),
              createRow('controlKey', '${fieldKey.controlKey ?? ''}'),
              createRow('flex', '${info.flex}'),
              createRow('inline', '${info.inline}'),
              createRow('readOnly', '${info.readOnly}'),
            ],
          ),
        )
      ],
    );
  });

project status

beta

develop plan

  1. performance test
  2. support more fields

Libraries

form_builder