Skip to content

injected_form_field

GIfatahTH edited this page Oct 2, 2021 · 1 revision

To work with other FormFields that have no TextEditingControler, We use RM.injectFormField method and it corresponding widget OnFormFieldBuilder.

Single CheckBox:

Let's start with a single CheckBox: First inject the state of bool type

final myCheckBox = RM.injectFormField<bool>(false);

Notice the initial value is false. It can be null if the state is nullable (this will impact how lableText, hintText are displayed).

RM.injectFormField has the same properties as in RM.injectTextEditing (for example validation and when to validate).

InjectedFormField<T> injectFormField<T>(
  T initialValue, {
  List<String? Function(T)>? validators,
  bool? validateOnValueChange,
  bool? validateOnLoseFocus,
  void Function(InjectedFormField<dynamic>)? onValueChange,
  bool autoDispose = true,
  bool isReadOnly = false,
  bool isEnabled = true,
})

See how to work with TextFields using InjectedTextEditing

In the widget tree and inside OnFormBuilder, we use OnFormFieldBuilder:

OnFormFieldBuilder<bool>(
   listenTo: myCheckBox,
   builder: (value, onChanged) {
     return CheckboxListTile(
       value: value,
       onChanged: onChanged,
       title: Text('I accept the licence'),
     );
   },
 ),

The builder method exposes the current value and the onChanged callback.

In most cases, any input form widget have a value and onChanged properties. You must set these properties to the exposed value and onChanged.

You can decorate your input using the inputDecoration property, which takes an InputDecoration.

OnFormFieldBuilder<bool>(
  listenTo: myCheckBox,
  inputDecoration: InputDecoration(
    hintText: 'Hint Text',
    labelText: 'Label Text',
    helperText: 'Helper Text',
  ),
  builder: (value, onChanged) {
    return CheckboxListTile(
      value: value,
      onChanged: onChanged,
      title: Text('I accept the licence'),
    );
  },
),

This is the result when state is successfully validated image

And this is the result when state is not validated.

image

Switch

final switcher = RM.injectFormField(false);

OnFormFieldBuilder<bool>(
  listenTo: switcher,
  inputDecoration: InputDecoration(
    labelText: 'switcher label',
    hintText: 'switcher hint',
    helperText: 'switcher helper text',
    suffixIcon: dropdownMenu.hasError
        ? const Icon(Icons.error, color: Colors.red)
        : const Icon(Icons.check, color: Colors.green),
  ),
  builder: (val, onChanged) {
    return SwitchListTile(
      value: val,
      onChanged: onChanged,
      title: Text('I Accept the terms and conditions'),
    );
  },
),

Date Picker

final dateTime = RM.injectFormField<DateTime?>(
  null,
  validators: [
    (date) {
      if (date == null || date.isAfter(DateTime.now())) {
        return 'Not allowed';
      }
    }
  ],
  validateOnLoseFocus: true,
);


OnFormFieldBuilder(
  listenTo: dateTime,
  inputDecoration: InputDecoration(
    labelText: 'DatePicker label',
    hintText: 'DatePicker hint',
    helperText: 'DatePicker helper text',
  ),
  builder: (value, onChanged) => ListTile(
    dense: true,
    title: Text('${value ?? ''}'),
    //clear the state
    trailing: IconButton(
      icon: Icon(Icons.clear),
      onPressed: () => dateTime.value = null,
    ),
    onTap: () async {
      final result = await showDatePicker(
        context: context,
        initialDate: dateTime.value ?? DateTime.now(),
        firstDate: DateTime(2000, 1, 1),
        lastDate: DateTime(2040, 1, 1),
      );
      if (result != null) {
        dateTime.value = result;
      }
    },
  ),
),

DateTimeRange picker

final dateTimeRange = RM.injectFormField<DateTimeRange?>(null);

OnFormFieldBuilder<DateTimeRange?>(
  listenTo: dateTimeRange,
  inputDecoration: InputDecoration(
    labelText: 'DateRangePicker label',
    hintText: 'DateRangePicker hint',
    helperText: 'DateRangePicker helper text',
  ),
  builder: (value, onChanged) {
    return ListTile(
      dense: true,
      title: Text('${value ?? ''}'),
      trailing: IconButton(
        icon: Icon(Icons.close),
        onPressed: () {
          dateTimeRange.value = null;
        },
      ),
      onTap: () async {
        final result = await showDateRangePicker(
          context: context,
          firstDate: DateTime(2000, 1, 1),
          lastDate: DateTime(2040, 1, 1),
        );
        if (result != null) {
          dateTimeRange.value = result;
        }
      },
    );
  },
),

Slider

final slider = RM.injectFormField<double>(
    6.0,
    validators: [
      (value) {
        if (value < 6.0) {
          return 'Not allowed';
        }
      }
    ],
  );
                  
OnFormFieldBuilder<double>(
  listenTo: slider,
  autofocus: true,
  inputDecoration: InputDecoration(
    labelText: 'Slider label',
    hintText: 'Slider hint',
    helperText: 'Slider helper text: ${slider.value}',
  ),
  builder: (value, onChanged) {
    return Slider(
      value: value,
      onChanged: onChanged,
      min: 0.0,
      max: 10.0,
    );
  },
),

RangeSlider

OnFormFieldBuilder<RangeValues>(
  listenTo: rangeSlider,
  inputDecoration: InputDecoration(
    labelText: 'Slider label',
    hintText: 'Slider hint',
    helperText: 'Slider helper text',
  ),
  builder: (value, onChanged) {
    return RangeSlider(
      values: value,
      onChanged: onChanged,
      min: 0.0,
      max: 100.0,
      divisions: 20,
    );
  },
),

DropDownMenu

const genders = ['Male', 'Female', 'Other'];
final dropdownMenu = RM.injectFormField<String?>(null);
OnFormFieldBuilder<String?>(
  listenTo: dropdownMenu,
  inputDecoration: InputDecoration(
    labelText: 'DropDownMenu label',
    hintText: 'DropDownMenu hint',
    helperText: 'DropDownMenu helper text',
    suffixIcon: dropdownMenu.hasError
        ? const Icon(Icons.error, color: Colors.red)
        : const Icon(Icons.check, color: Colors.green),
  ),
  builder: (val, onChanged) {
    return DropdownButtonHideUnderline(
      child: DropdownButton<String>(
        value: val,
        items: genders
            .map(
              (gender) => DropdownMenuItem(
                value: gender,
                child: Text(gender),
              ),
            )
            .toList(),
        onChanged: onChanged,
      ),
    );
  },
),

Radio Buttons

final radioOptions = ['Dart', 'Kotlin', 'Java', 'Swift', 'Objective-C'];
final radioButtons = RM.injectFormField<String>('');
OnFormFieldBuilder<String>(
  listenTo: radioButtons,
  inputDecoration: InputDecoration(
    labelText: 'Radio buttons label',
    hintText: 'Radio buttons hint',
    helperText: 'Radio buttons helper text',
    suffixIcon: radioButtons.hasError
        ? const Icon(Icons.error, color: Colors.red)
        : const Icon(Icons.check, color: Colors.green),
  ),
  builder: (val, onChanged) {
    return Row(
      children: radioOptions
          .map(
            (e) => InkWell(
              onTap: () => radioButtons.onChanged(e),
              child: Row(
                children: [
                  Radio<String>(
                    value: e,
                    groupValue: val,
                    onChanged: onChanged,
                  ),
                  Text(e),
                  const SizedBox(width: 8),
                ],
              ),
            ),
          )
          .toList(),
    );
  },
),

Multi CheckBoxes

final options = ['Dart', 'Kotlin', 'Java', 'Swift', 'Objective-C'];

final multiCheckBoxes = RM.injectFormField<List<String>>(
    [],
    validators: [
      (val) {
        if (val.length < 3) {
          return 'choose more than three items';
        }
      }
    ],
  );


OnFormFieldBuilder<List<String>>(
  listenTo: multiCheckBoxes,
  inputDecoration: InputDecoration(
    labelText: 'multiCheckBoxes label',
    hintText: 'multiCheckBoxes hint',
    helperText: 'multiCheckBoxes helper text',
  ),
  builder: (val, onChanged) {
    return Row(
      children: radioOptions
          .map(
            (e) => Row(
                children: [
                  Checkbox(
                    value: val.contains(e),
                    onChanged: (checked) {
                      if (checked!) {
                        multiCheckBoxes.value = [...val, e];
                      } else {
                        multiCheckBoxes.value =
                            val.where((el) => e != el).toList();
                      }
                    },
                  ),
                  Text(e),
                  const SizedBox(width: 8),
                ],
              ),
          )
          .toList(),
    );
  },
),

Here is the final picture image