How to use reactive_forms ReactiveFormArray in flutter?

3k views Asked by At

I was trying to create dynamic array of fields in flutter, reactive_forms was best choice. But there is no documentation for ReactiveFormArray which i need to use.

Did some trial and error and found the way to use it. Below is the answer.

2

There are 2 answers

1
Mohemmed Niyaz On

Since there is no documentation i could find. Sharing so it may be useful for someone.

1.Create a reactive form with FormArray

You can also add a formgroup in the formarray if you need it by default.

    final form = FormGroup({
        'itemName': FormControl(value: ''),
        'prices': FormArray([]),
      });

2.Get the FormArray We will use this list to add multiple form groups dynamically.

    FormArray get pricesList => form.control('prices') as FormArray;

3.Initiate data in init method Dynamically adding a form array data.

    @override
      void initState() {
        pricesList.add(FormGroup({
          'greaterThan': FormControl<int>(value: 0),
          'lessThan': FormControl<int>(value: 10),
          'price': FormControl<int>(value: 0),
        }));
        super.initState();
      }
  1. Add in ReactiveForm return the form to your widget
    ReactiveForm(
                  formGroup: this.form,
                  child: Column(
                    children: <Widget>[
                      ReactiveTextField(
                        formControlName: 'itemName',
                        decoration: InputDecoration(
                          labelText: 'Item Name',
                        ),
                      ),
                      ReactiveFormArray(
                          formArrayName: 'prices',
                          builder: (context, formArray, child) {
                            final cities = pricesList.controls
                                .map((control) => control as FormGroup)
                                .map((currentform) {
                              return ReactiveForm(
                                  formGroup: currentform,
                                  child: Column(
                                    children: <Widget>[
                                      ReactiveTextField(
                                        formControlName: 'greaterThan',
                                        decoration: InputDecoration(
                                          labelText: 'greater than',
                                        ),
                                      ),
                                      ReactiveTextField(
                                        formControlName: 'lessThan',
                                        decoration: InputDecoration(
                                          labelText: 'less than',
                                        ),
                                      ),
                                      ReactiveTextField(
                                        formControlName: 'price',
                                        decoration: InputDecoration(
                                          labelText: 'Price',
                                        ),
                                      ),
                                    ],
                                  ));
                            });
                            return Wrap(
                              runSpacing: 20,
                              children: cities.toList(),
                            );
                          }),
                      ReactiveFormConsumer(
                        builder: (context, form, child) {
                          return RaisedButton(
                            child: Text('Submit'),
                            onPressed: form.valid ? _onSubmit : null,
                          );
                        },
                      ),
                    ],
                  ),
                ),
              ));
  1. Dynamically add in Form array when you call this method, dynamically more formarray fields will be added
    addFormArray() async {
        pricesList.add(FormGroup({
          'greaterThan': FormControl<int>(value: 10),
          'lessThan': FormControl<int>(value: 50),
          'price': FormControl<int>(value: 0),
        }));
        setState(() {});
      }

Simply call this method on a button click or wherever you need.

0
smfog On

I spent months trying to figure out why my very similar, however slightly different code, gave all the dynamically added controls the same value. Here is me hoping you won't do the same

The difference between the example and my own code was that I used a variable to hold the form I wanted to add. Giving me the code below

var price = FormGroup({
          'greaterThan': FormControl<int>(value: 0),
          'lessThan': FormControl<int>(value: 10),
          'price': FormControl<int>(value: 0),
        })

@override
      void initState() {
        pricesList.add(price);
        super.initState();
      }
    
...

addFormArray() async {
        pricesList.add(price);
        setState(() {});
      }

What this does is adding the same instance of price to the formarray multiple times. So even if you use the index to point to the correct item in the array, it will change the value of all the elements. Because they are all the same!

What you need to do is add a new instance of price to each item in the list. As in the answer to this question or like this if you want to do it with less code.

FormGroup createPriceFormGroup(){
 return FormGroup({
          'greaterThan': FormControl<int>(value: 0),
          'lessThan': FormControl<int>(value: 10),
          'price': FormControl<int>(value: 0),
        })
}

@override
      void initState() {
        pricesList.add(createPriceFormGroup());
        super.initState();
      }
    
...

addFormArray() async {
        pricesList.add(createPriceFormGroup());
        setState(() {});
      }