Search code examples
androidiosflutterdartflutter-form-builder

How to pass a list of maps as inital value to the FormBuilderFilterChip in flutter_form_builder package?


I have tried passing a list of the same 'value' field of the FormBuilderFieldOption to the initialValue argument.

Edit: Passing the list satisfies the 'required' validator but does not show the chips as 'marked'

Now if i'm not wrong this should return a set of chips that are all marked, but it does not.

List<Map<String,dynamic>> _allValues = [
  {id: 1586762938154, name: 202, rate: 5000, type: 'ABYSS'},
  {id: 1586759232569, name: 101, rate: 1000, type: 'DELUXE'},
  {id: 1586849439323, name: 13, rate: 3434, type: 'DELUXE'},
  {id: 1586759258120, name: 102, rate: 2000, type: 'EXECUTIVE'},
  {id: 1586779416843, name: 103, rate: 2343, type: 'EXECUTIVE'},
]
FormBuilderFilterChip(
  initialValue: _allValues.map((value) => value).toList(),
  attribute: 'filter_chip',
  decoration: InputDecoration(
      labelText: 'Select many options',
    ),
  options: _allValues
               .map((val) => FormBuilderFieldOption(
                   value: val,
                   child: Text(val['name']),
                  ),
                ).toList(),
  onChanged: (value) {
    _selected = value;
     print(_selected);
   },
 )

Solution

  • I took a look under the hood and how the FormBuilderFilterChip marks the chips as selected is by using the List.contains() method.

    In this method the equality used to determine whether [element] is equal to an element of the List defaults to the [Object.==] of the element.

    So to tackle this i built my own custom FilterChipField (borrowing most of the necessary code from the FormBuilderFilterChip)

    FormBuilderCustomField(
      attribute: "name",
      validators: [
        FormBuilderValidators.required(),
      ],
      formField: FormField(
        enabled: true,
        builder: (FormFieldState<dynamic> field) {
          return InputDecorator(
            decoration: InputDecoration(
              prefixIcon: Icon(Icons.vpn_key),
              labelText: "Assign Room(s)",
              contentPadding: EdgeInsets.only(top: 10.0, bottom: 0.0),
              errorText: field.errorText,
            ),
            child: Container(
              child: _buildChipSelectField(field),
            ),
          );
        },
      ),
    )
    

    The _buildChipSelectField below contains two custom functions one (_selectedValuesContains) to check the equality of the object in the lists and second (_selectedValuesRemove) to remove the object on toggling the chip

    Widget _buildChipSelectField(FormFieldState<dynamic> field) {
      return Wrap(
        spacing: 3.0,
        children: _allValues.map((item) {
          return FilterChip(
            label: Text("${item['name']} - ${item['type']}"),
            selectedColor: Colors.black38,
            selected: _selectedValuesContains(item),
            onSelected: (value) {
              setState(() {
                if (_selectedValuesContains(item)) {
                  _selectedValuesRemove(item);
                } else {
                  _selectedValues.add(item);
                }
                field.didChange(_selectedValues);
              });
            },
          );
        }).toList(),
      );
    }
    

    (These methods are for my sample data (i.e _allValues), but the idea is very basic in nature.)

    _selectedValuesContains

    bool _selectedValuesContains(Map item) {
      int index = _selectedValues.indexWhere((val) => val['id'] == item['id']);
      return index >= 0 ? true : false;
    }
    

    _selectedValuesRemove

    void _selectedValuesRemove(Map item) {
      int index = _selectedValues.indexWhere((val) => val['id'] == item['id']);
      _selectedValues.removeAt(index);
    }