flutter_fast_forms 18.1.0  flutter_fast_forms: ^18.1.0 copied to clipboard
flutter_fast_forms: ^18.1.0 copied to clipboard
Flutter Fast Forms is the only Dart package you need to build Flutter forms fast.
Flutter Fast Forms #
Flutter Fast Forms is the only Dart package you need to build Flutter forms fast.
It adds these missing features to the Flutter SDK:
- FastFormControl<T>convenience widgets that wrap Material / Cupertino form controls in a- FormField<T>according to the already built-in- TextFormField/- DropdownButtonFormField
- FastFormwidget that wraps the built-in- Formwidget for providing the current form field values in- onChangedcallback
- FastFormArraywidget that aggregates a flexible number of homogeneous controls in a single- FormField<T>
- FastChipsInputwidget that converts text input into chips as defined by Material Design
- Conditional form fields
- touchedvalidation state
- Common FormFieldValidator<T>functions



Table of Contents #
Getting Started #
1. Add a FastForm to your widget tree:
class MyFormPage extends StatelessWidget {
  MyFormPage({Key? key, required this.title}) : super(key: key);
  final formKey = GlobalKey<FormState>();
  final String title;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          child: FastForm(
            formKey: formKey,
            children: [],
          ),
        ),
      ),
    );
  }
}
2. Add FastFormControl<T> children to the FastForm:
FastForm(
  formKey: formKey,
  children: [
    const FastTextField(
      name: 'field_destination',
      labelText: 'Destination',
      placeholder: 'Where are you going?',
    ),
    FastDateRangePicker(
      name: 'field_check_in_out',
      labelText: 'Check-in - Check-out',
      firstDate: DateTime.now(),
      lastDate: DateTime.now().add(const Duration(days: 365)),
    ),
    const FastCheckbox(
      name: 'field_travel_purpose',
      labelText: 'Travel purpose',
      titleText: 'I am travelling for work',
    ),
  ],
),
3. Wrap the children in a FastFormSection for visual grouping and consistent padding:
FastForm(
  formKey: formKey,
  children: [
    FastFormSection(
      header: const Text('My Form'),
      padding: EdgeInsets.all(16.0),
      children: [
        const FastTextField(
          name: 'field_destination',
          labelText: 'Destination',
          placeholder: 'Where are you going?',
        ),
        // ...
      ],
    ),
  ]
),
Widget Catalog #
| FastFormControl<T> | field value type | wraps Material widget | wraps Cupertino widget when adaptive: true | 
|---|---|---|---|
| FastAutocomplete<O> | String | Autocomplete<O> | no | 
| FastCheckbox | bool | CheckboxListTile | CupertinoCheckbox | 
| FastChoiceChips<T> | Set<T> | ChoiceChip | no | 
| FastCalendar | DateTime | CalendarDatePicker | no | 
| FastChipsInput | List<String> | RawAutocomplete<String>+InputChip | no | 
| FastDatePicker | DateTime | showDatePicker | CupertinoDatePicker | 
| FastDateRangePicker | DateTimeRange | showDateRangePicker | no | 
| FastDropdown<T> | T | DropdownButtonFormField<T> | no | 
| FastRadioGroup<T> | T | RadioListTile<T> | no | 
| FastRangeSlider | RangeValues | RangeSlider | no | 
| FastSegmentedButton<T> | Set<T> | SegmentedButton<T> | no | 
| FastSegmentedControl<T> | T extends Object | no | CupertinoSlidingSegmentedControl<T> | 
| FastSlider | double | Slider.adaptive | CupertinoSlider | 
| FastSwitch | bool | SwitchListTile | CupertinoSwitch | 
| FastTextField | String | TextFormField | CupertinoTextFormFieldRow | 
| FastTimePicker | TimeOfDay | showTimePicker | no use FastDatePickerwithCupertinoDatePickerMode.time | 
Adaptive Form Fields #
While some form controls are unique to a certain platform, various others are present in multiple design languages.
By default, Flutter Fast Forms uses Material widgets on any platform.
This behavior is adjustable so that platform-specific Cupertino widgets are automatically rendered on iOS.
Tip
The widget catalog tells you which FastFormControl is adaptive.
📓 Example: Always use Cupertino widgets on iOS in a FastForm.
FastForm(
  formKey: formKey,
  adaptive: true,
  children: [
    const FastSwitch(
      name: 'switch',
      titleText: 'Disable text field',
    ),
    FastTextField(
      name: 'text_field',
      labelText: 'Just some sample text field',
    ),    
  ]
),
Note
- When adaptiveis set totrueany built-inFormFieldBuilderreturns a corresponding Cupertino widget on iOS, if it exists.
📓 Example: Only use the Cupertino widget on iOS for a dedicated FastSwitch.
FastForm(
  formKey: formKey,
  children: [
    const FastSwitch(
      name: 'switch',
      adaptive: true,
      titleText: 'Disable text field',
    ),
  ]
),
Conditional Form Fields #
Not all controls in a form are autonomous and act independent of each other.
Occasionally, the state of a form field might be directly related to the state of some other form field as well.
Flutter Fast Forms allows you to define such conditions declaratively.
📓 Example: A FastTextField that is disabled when a FastSwitch is selected.
1. Add the conditions property to the conditional form field and assign an empty Map:
const FastSwitch(
  name: 'switch',
  titleText: 'Disable text field',
),
FastTextField(
  name: 'text_field',
  labelText: 'Just some sample text field',
  conditions: {},
),
2. Choose a suitable FastConditionHandler as Map key and assign a FastConditionList:
const FastSwitch(
  name: 'switch',
  titleText: 'Disable text field when selected',
),
FastTextField(
  name: 'text_field',
  labelText: 'Just some sample text field',
  conditions: {
    FastCondition.disabled: FastConditionList([]),
  },
)
Note
A FastConditionHandler is a function that runs whenever a FastConditionList is checked and determines what happens when the condition is either met or not.
3. Add a FastCondition relating the field to another field:
const FastSwitch(
  name: 'switch',
  titleText: 'Disable text field when selected',
),
FastTextField(
  name: 'text_field',
  labelText: 'Just some sample text field',
  conditions: {
    FastCondition.disabled: FastConditionList([
      FastCondition(
        target: 'switch',
        test: (value, field) => value is bool && value,
      ),
    ]),
  },
),
Note
target is the name of the FastFormField that the form field depends on.
📓 Example: A FastTextField that is enabled when a FastSwitch or a FastCheckbox is selected.
const FastCheckbox(
  name: 'checkbox',
  titleText: 'Enable text field when selected',
),
const FastSwitch(
  name: 'switch',
  titleText: 'Enable text field when selected',
),
FastTextField(
  name: 'text_field',
  enabled: false,
  labelText: 'Just some sample text field',
  conditions: {
    FastCondition.enabled: FastConditionList([
      FastCondition(
        target: 'switch',
        test: (value, field) => value is bool && value,
      ),
      FastCondition(
        target: 'checkbox',
        test: (value, field) => value is bool && value,
      ),
    ]),
  },
),
📓 Example: A FastTextField that is disabled when both a FastSwitch and a FastCheckbox are selected.
const FastCheckbox(
  name: 'checkbox',
  titleText: 'Disable text field when selected',
),
const FastSwitch(
  name: 'switch',
  titleText: 'Disable text field when selected',
),
FastTextField(
  name: 'text_field',
  labelText: 'Just some sample text field',
  conditions: {
    FastCondition.enabled: FastConditionList(
      [
        FastCondition(
          target: 'switch',
          test: (value, field) => value is bool && value,
        ),
        FastCondition(
          target: 'checkbox',
          test: (value, field) => value is bool && value,
        ),
      ],
      match: FastConditionMatch.every,
    ),
  },
),
Note
match specifies how all individual test results in the list are evaluated to determine whether the condition is met.
Custom Form Fields #
There are use cases where the widget catalog does not fully satisfy your individual requirements.
As a consequence you have to add non-standard controls to your form.
With Flutter Fast Forms you're free to wrap any custom widget into a form field.
📓 Example: A simple widget that provides a random integer whenever a button is pressed.
1. Create a stateful widget class extending FastFormField<T> with a corresponding FastFormFieldState<T>:
class MyCustomField extends FastFormField<int> {
  const MyCustomField({
    super.builder = myCustomFormFieldBuilder,
    super.key,
    required super.name,
  });
  @override
  MyCustomFieldState createState() => MyCustomFieldState();
}
class MyCustomFieldState extends FastFormFieldState<int> {
  @override
  MyCustomField get widget => super.widget as MyCustomField;
}
Note
- builderand- nameare required constructor parameters of- FastFormField.
- builderis a standard Flutter- FormFieldBuilder<T>.
2. Implement the FormFieldBuilder<T> returning your custom widget:
Widget myCustomFormFieldBuilder(FormFieldState<int> field) {
  field as MyCustomFieldState;
  final MyCustomFieldState(:decoration, :didChange, :value) = field;
  return InputDecorator(
    decoration: decoration,
    child: Row(
      children: [
        ElevatedButton(
          child: const Text('Create random number'),
          onPressed: () => didChange(Random().nextInt(1 << 32)),
        ),
        if (value is int)
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 12.0),
            child: Text(value.toString()),
          )
      ],
    ),
  );
}
Note
- Casting fieldis mandatory to accessFastFormFieldproperties and functions.
- Always call field.didChange()to update the value of the form field.
3. Add all super-initializer parameters that the form field should support:
class MyCustomField extends FastFormField<int> {
  const MyCustomField({
    super.builder = myCustomFormFieldBuilder,
    super.decoration,
    super.enabled,
    super.helperText,
    super.initialValue,
    super.key,
    super.labelText,
    required super.name,
    super.onChanged,
    super.onReset,
    super.onSaved,
    super.onTouched,
    super.validator,
  });
  @override
  MyCustomFieldState createState() => MyCustomFieldState();
}
Note
Always make sure that you apply certain super-initializer parameters like decoration or enabled in your builder functions.
Otherwise assigning those arguments when invoking the constructor won't have any effect.