Why reactiveTextFields doesn't work well when in different class than FormGroup?

1.2k views Asked by At

I have a class where I initialized FormGroup:

final form = FormGroup(
    {
      'oldPassword': FormControl<String>(
        value: null,
        validators: [
          Validators.required,
        ],
      ),
      'newPassword': FormControl<String>(
        value: null,
        touched: true,
        validators: [
          Validators.pattern(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$',
              validationMessage:
                  'Required: \n * One uppercase and one downcase letter \n * Atleast one number \n * Atleast 8 characters')
        ],
      ),
      'repeatPassword': FormControl<String>(
        value: null,
        validators: [
          Validators.required,
        ],
      ),
    },
    validators: [
      Validators.mustMatch(
        'newPassword',
        'repeatPassword',
      )
    ],
  );

I created a StatefullWidget class ReactivePasswordField:

import 'package:flutter/material.dart';
import 'package:reactive_forms/reactive_forms.dart';

class ReactivePasswordField extends StatefulWidget {
  final String controlName;
  final String hintText;
  final Function() onSubmitted;

  const ReactivePasswordField({
    required this.controlName,
    required this.hintText,
    required this.onSubmitted,
  });

  @override
  _ReactivePasswordFieldState createState() => _ReactivePasswordFieldState();
}

class _ReactivePasswordFieldState extends State<ReactivePasswordField> {
  bool isObscure = true;
  IconData icon = Icons.visibility_off;
  late String controlName;

  @override
  void initState() {
    controlName = widget.controlName;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ReactiveTextField(
      autofocus: true,
      obscureText: isObscure,
      formControlName: 'oldPassword',
      onSubmitted: () => widget.onSubmitted,
      validationMessages: (control) => {
        'required': 'Field cannot be empty.',
        'mustMatch': 'New password must match.'
      },
      decoration: InputDecoration(
        filled: true,
        prefixIcon: Icon(
          Icons.lock,
          color: Color(0xffe96cbd),
        ),
        hintText: widget.hintText,
        suffixIcon: GestureDetector(
            onTap: () {
              if (isObscure) {
                setState(() {
                  icon = Icons.visibility_off;
                  isObscure = false;
                });
              } else {
                setState(() {
                  icon = Icons.visibility;
                  isObscure = true;
                });
              }
            },
            child: Icon(
              icon,
              color: Color(0xff3A4A78),
            )),
        border: OutlineInputBorder(
          borderSide: BorderSide.none,
          borderRadius: BorderRadius.circular(20.0),
        ),
      ),
    );
  }
}

I did it, because i want every ReactivePasswordField to have own state to manage showing and hiding input view. The problem is when I input to one of ReactivePasswordField the others have same input. My Validators also doesn't work. How can i fix it?

There is how i use my ReactivePasswordFields in a class where i initialized FormGroup:

SingleChildScrollView(
          child: ReactiveForm(
        formGroup: form,
        child: Column(
          children: [
            ReactivePasswordField(
                controlName: 'oldPassword',
                hintText: 'Current password',
                onSubmitted: () => form.focus('newPassword')),
            Padding(
              padding: const EdgeInsets.symmetric(vertical: 8.0),
              child: ReactivePasswordField(
                controlName: 'newPassword',
                hintText: 'New password',
                onSubmitted: () => form.focus('repeatPassword'),
              ),
            ),
            ReactivePasswordField(
              controlName: 'repeatPassword',
              hintText: 'Repeat password',
              onSubmitted: () => {},
            )
          ],
        ),
      )),
1

There are 1 answers

0
Darexad On BEST ANSWER

I just messed up in my ReactivePasswordField class. There are the changes:

import 'package:flutter/material.dart';
import 'package:reactive_forms/reactive_forms.dart';

class ReactivePasswordField extends StatefulWidget {
  final String controlName;
  final String hintText;
  final Function() onSubmitted;

  const ReactivePasswordField({
    required this.controlName,
    required this.hintText,
    required this.onSubmitted,
  });

  @override
  _ReactivePasswordFieldState createState() => _ReactivePasswordFieldState();
}

class _ReactivePasswordFieldState extends State<ReactivePasswordField> {
  bool isObscure = true;
  IconData icon = Icons.visibility_off;


  @override
  Widget build(BuildContext context) {
    return ReactiveTextField(
      autofocus: true,
      obscureText: isObscure,

//-------------CHANGE--------------------------------------------------------
//Here i had static String instead of widget.controlName 
      formControlName: widget.controlName,
      onSubmitted: () => widget.onSubmitted,
      validationMessages: (control) => {
        'required': 'Field cannot be empty.',
        'mustMatch': 'New password must match.'
      },
      decoration: InputDecoration(
        filled: true,
        prefixIcon: Icon(
          Icons.lock,
          color: Color(0xffe96cbd),
        ),
        hintText: widget.hintText,
        suffixIcon: GestureDetector(
            onTap: () {
              if (isObscure) {
                setState(() {
                  icon = Icons.visibility_off;
                  isObscure = false;
                });
              } else {
                setState(() {
                  icon = Icons.visibility;
                  isObscure = true;
                });
              }
            },
            child: Icon(
              icon,
              color: Color(0xff3A4A78),
            )),
        border: OutlineInputBorder(
          borderSide: BorderSide.none,
          borderRadius: BorderRadius.circular(20.0),
        ),
      ),
    );
  }
}