Flutter conditional rendering of 2 forms - duplicates the form values to the other form

893 views Asked by At

I'm trying to do conditional rendering of two forms, using flutter reactive_forms. But when I type into one form, then click a button which renders the other form in the exact same place, the same values remain in the other form as well. The only thing that changes in the form control labels. The forms just become a duplicate of each other. I've tried so many things to get the values not to duplicate, to no avail. One of the worst programming experiences I've had for a long time.

Here is my FormGroup which I was initially using for both forms because I do want most of the fields to duplicate across, just not all of them, such as "groceryStore" and "restaurant", are not shared between the two forms. I have even tried using a separate FormGroup for each with all completely unique field names... which I assumed would be the cause of the duplication initially but when changing to two separate FormGroup's with all different names it did not help. It's like it is in the same place on the page as the other conditional form field, so it will just keep its value. When I leave the page and return to it the form that I view first has the correct, different values. My code:

  FormGroup form = FormGroup({
    VeganItemFieldNames.name:
        FormControl<String>(validators: [Validators.required]),
    VeganItemFieldNames.brand:
        FormControl<String>(validators: [Validators.required]),
    VeganItemFieldNames.description:
        FormControl<String>(validators: [Validators.required]),
    VeganItemFieldNames.price:
        FormControl<double>(validators: [Validators.required]),
    VeganItemFieldNames.groceryStore:
        FormControl<String>(validators: [Validators.required]),
    VeganItemFieldNames.restaurant:
        FormControl<String>(validators: [Validators.required]),
    // VeganItemFieldNames.groceryItemCategories: FormControl<String>(),
    // VeganItemFieldNames.menuItemCategories: FormControl<String>(),
    VeganItemFieldNames.image:
        FormControl<String>(validators: [Validators.required]),
  });

My view:

child: ReactiveForm(
                      formGroup: viewModelState.form,
                      child: Column(
                          mainAxisSize: MainAxisSize.min,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Expanded(
                                flex: 7,
                                child: KeyboardAvoider(
                                    autoScroll: true,
                                    child: Column(
                                        crossAxisAlignment:
                                            CrossAxisAlignment.center,
                                        mainAxisAlignment:
                                            MainAxisAlignment.start,
                                        children: [
                                          if (viewModel.addVeganItemStrategy !=
                                              null)
                                            ...viewModel.buildFields(context)
                                        ]))),
                            Container(
                                padding: const EdgeInsets.fromLTRB(
                                    PAD_0, PAD_1, PAD_0, PAD_3),
                                child: Center(
                                    child: VpSubmitButton(
                                        text: 'Submit', onPressed: () {})))
                          ]))

The ViewModel:

  List<Widget> buildFields(BuildContext context1) {
    return addVeganItemStrategy.buildFields(
        context: context, form: viewModelState.form);
  }

one strategy buildFields()

class AddGroceryItemStrategy implements AddVeganItemStrategy {
  List<Widget> buildFields({BuildContext context, FormGroup form}) {
    return [
      nameTextField(context: context, form: form),
      brandTextField(context: context),
      groceryStoreTextField(context: context),
      descriptionTextField(context: context),
      priceTextField(context: context),
      //   groceryItemCategoriesTextField(context: context, form: form),
      imageTextField(context: context),
    ];
  }

  Widget nameTextField(
      {BuildContext context, FormGroup form, String nextField}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_4, PAD_3_HALF, PAD_4),
        child: VpTextField(
            context: context,
            labelText: 'Name',
            maxLength: 40,
            formControlName: GroceryItemFieldNames.name,
            hintText: 'Name...',
            form: form,
            textInputAction: TextInputAction.next,
            nextField: nextField,
            validationMessages: (control) => {
                  'required': 'Please enter the Product Name',
                }));
  }

  Widget brandTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          labelText: 'Brand',
          maxLength: 30,
          formControlName: GroceryItemFieldNames.brand,
          hintText: 'Brand...',
          validationMessages: (control) =>
              {'required': 'Please enter the Product Brand'},
        ));
  }

  Widget groceryStoreTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          labelText: 'Grocery Store',
          formControlName: GroceryItemFieldNames.groceryStore,
          hintText: 'Grocery Store...',
          validationMessages: (control) =>
              {'required': 'Please enter the Grocery Store'},
        ));
  }

  Widget descriptionTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          maxLength: 150,
          labelText: 'Description',
          formControlName: GroceryItemFieldNames.description,
          hintText: 'Description...',
          validationMessages: (control) =>
              {'required': 'Please enter the Product Description'},
        ));
  }

  Widget priceTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          textInputType: TextInputType.number,
          context: context,
          labelText: 'Price',
          formControlName: GroceryItemFieldNames.price,
          hintText: 'Price...',
          validationMessages: (control) =>
              {'required': 'Please enter the Product Price'},
        ));
  }

  Widget groceryItemCategoriesTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          labelText: 'Categories',
          formControlName: GroceryItemFieldNames.groceryItemCategories,
          hintText: 'Categories...',
          validationMessages: (control) =>
              {'required': 'Please enter the Product Categories'},
        ));
  }

  Widget imageTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          labelText: 'Image',
          formControlName: GroceryItemFieldNames.image,
          hintText: 'Image...',
          validationMessages: (control) =>
              {'required': 'Please enter the Product Image'},
        ));
  }
}

Another strategy buildFields:

class AddMenuItemStrategy implements AddVeganItemStrategy {
  List<Widget> buildFields({BuildContext context, FormGroup form}) {
    return [
      nameTextField(context: context, form: form),
      restaurantTextField(context: context),
      descriptionTextField(context: context),
      priceTextField(context: context),
      //   menuItemCategoriesTextField(context: context, form: form),
      imageTextField(context: context),
    ];
  }

  Widget nameTextField(
      {BuildContext context, FormGroup form, String nextField}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_4, PAD_3_HALF, PAD_4),
        child: VpTextField(
            context: context,
            labelText: 'Name',
            maxLength: 40,
            formControlName: MenuItemFieldNames.name,
            hintText: 'Name...',
            form: form,
            textInputAction: TextInputAction.next,
            nextField: nextField,
            validationMessages: (control) => {
                  'required': 'Please enter the Product Name',
                }));
  }

  Widget restaurantTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          labelText: 'Restaurant',
          formControlName: MenuItemFieldNames.restaurant,
          hintText: 'Restaurant...',
          validationMessages: (control) =>
              {'required': 'Please enter the Restaurant'},
        ));
  }

  Widget descriptionTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          maxLength: 150,
          labelText: 'Description',
          formControlName: MenuItemFieldNames.description,
          hintText: 'Description...',
          validationMessages: (control) =>
              {'required': 'Please enter the Menu Item Description'},
        ));
  }

  Widget priceTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          textInputType: TextInputType.number,
          context: context,
          labelText: 'Price',
          formControlName: MenuItemFieldNames.price,
          hintText: 'Price...',
          validationMessages: (control) =>
              {'required': 'Please enter the Menu Item Price'},
        ));
  }

  Widget menuItemCategoriesTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          labelText: 'Categories',
          formControlName: MenuItemFieldNames.menuItemCategories,
          hintText: 'Categories...',
          validationMessages: (control) =>
              {'required': 'Please enter the Menu Item Categories'},
        ));
  }

  Widget imageTextField({BuildContext context}) {
    return Container(
        padding:
            const EdgeInsets.fromLTRB(PAD_3_HALF, PAD_0, PAD_3_HALF, PAD_4),
        child: VpTextField(
          context: context,
          labelText: 'Image',
          formControlName: MenuItemFieldNames.image,
          hintText: 'Image...',
          validationMessages: (control) =>
              {'required': 'Please enter the Menu Item Image'},
        ));
  }
1

There are 1 answers

1
quoci On BEST ANSWER

I think that I had a similar problem like you. I solved it by adding a key to my ReactiveTextField.
Here you can see my issue https://github.com/joanpablo/reactive_forms/issues/66