form_builder_validators icon indicating copy to clipboard operation
form_builder_validators copied to clipboard

FormBuilderValidators.equal() do not update value to compare

Open deandreamatias opened this issue 2 years ago • 4 comments

Discussed in https://github.com/flutter-form-builder-ecosystem/form_builder_validators/discussions/61

Originally posted by orbiteleven August 30, 2023 Using form_builder_validators with flutter_form_builder and I have what I think should be a pretty common password+confirm password form, but I'm a bit confused. This is what I have so far:

FormBuilderTextField(
    name: 'password',
    decoration: InputDecoration(labelText: 'Password'),
    obscureText: true,
    validator: FormBuilderValidators.compose([
      FormBuilderValidators.required(),
      FormBuilderValidators.minLength(6)
    ])),
FormBuilderTextField(
    name: 'confirmPassword',
    decoration: InputDecoration(labelText: 'Confirm Password'),
    obscureText: true,
    validator: FormBuilderValidators.compose([
      FormBuilderValidators.required(),
      FormBuilderValidators.minLength(6),
      (value) {
        print(
            "value: $value matches ${_formKey.currentState?.instantValue['password']}");
        return null;
      },
      FormBuilderValidators.equal(
          _formKey.currentState?.instantValue['password'] ?? '',
          errorText: 'Passwords do not match')
    ])),

the "custom" validator works fine, but FormBuilderValidators.equal() throws an error. What am I doing wrong?

For some reason, FormBuilderValidators.equal validator don't update value to compare with fieldValue A nice way to show the error, is create a validate method with print:

FormFieldValidator<T> equal<T>(
  Object? value, {
  String? errorText,
}) =>
    (T? valueCandidate) {
      print(
        'value: $value valueCandidate: $valueCandidate'
      );
 // Value is null and valueCandidate the form field value, when update field value
      return valueCandidate != value ? errorText : null;
    };

deandreamatias avatar Sep 15 '23 22:09 deandreamatias

Unfortunately I don't have a solution, but here is what I found out. Apparently he does not get the new value or any value for the matching value. The cascading thing which keeps on going. I don't know what could cause that. image

When I add a setState() to the confirmPassword field on the onChange callback it works fine. image

Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(title: const Text('Form Builder Validators')),
     body: Padding(
       padding: const EdgeInsets.all(8.0),
       child: FormBuilder(
         key: _formKey,
         child: Column(
           children: <Widget>[
             FormBuilderTextField(
                 name: 'password',
                 initialValue: 'MyAwesomePassword',
                 decoration: InputDecoration(labelText: 'Password'),
                 obscureText: true,
                 autovalidateMode: AutovalidateMode.onUserInteraction,
                 validator: FormBuilderValidators.compose([
                   FormBuilderValidators.required(),
                   FormBuilderValidators.minLength(6)
                 ])),
             FormBuilderTextField(
                 name: 'confirmPassword',
                 decoration: InputDecoration(labelText: 'Confirm Password'),
                 obscureText: true,
                 autovalidateMode: AutovalidateMode.onUserInteraction,
                 onChanged: (value) => {
                       setState(() {
                         print(value);
                       })
                     },
                 validator: FormBuilderValidators.compose([
                   FormBuilderValidators.required(),
                   FormBuilderValidators.minLength(6),
                   (value) {
                     var password =
                         _formKey.currentState?.instantValue['password'];
                     print(
                         "value: $value matches ${_formKey.currentState?.instantValue['password']}");
                     return null;
                   },
                   FormBuilderValidators.equal(
                       _formKey.currentState?.instantValue['password'] ?? '',
                       errorText: 'Passwords do not match second one'),
                 ])),
           ],
         ),
       ),
     ),
   );

This is the Widget with what I tested. What seems to also works is this:

(value) {
  return FormBuilderValidators.equal<String>(
    _formKey.currentState?.instantValue['password'] ?? '',
    errorText: 'Passwords do not match first one')(value);
},

I think that the equal Funkton a Closure returns which saves the initial value (also tried it with another value and then I don't get the weird cascading String). Since the State is never set in the Component the value is never reevaluated.

I hope this helps, if anything is unclear please ask me anything and I will try to help.

the-devbear avatar Nov 10 '23 09:11 the-devbear

Is there an update about this? As I see this is still an issue in the latest version 11.0.0 ?

petodavid avatar Jul 15 '24 10:07 petodavid

i also have the same problem i now created my own code for this.

Johannes-Schiel avatar Feb 13 '25 19:02 Johannes-Schiel

i also have the same problem i now created my own code for this.

You can use my solution

 FormBuilderTextField(
    name: 'password',
    validator: FormBuilderValidators.password(),
    obscureText: true,
    decoration: InputDecoration(
      labelText: 'Password',
      hintText: 'Enter your password',
      prefixIcon: const Icon(Icons.lock),
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(16),
      ),
    ),
    style: Theme.of(context).textTheme.bodyLarge?.copyWith(
          fontWeight: FontWeight.w600,
        ),
  ),
  FormBuilderTextField(
    name: "confirm_password",
    decoration: InputDecoration(
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      labelText: "Confirm password",
      prefixIcon: const Icon(Icons.lock),
      suffixIcon: IconButton(
        onPressed: () {},
        icon: const Icon(Icons.remove_red_eye),
      ),
    ),
    obscureText: true,
    keyboardType: TextInputType.visiblePassword,
    validator: FormBuilderValidators.compose([
      FormBuilderValidators.required(),
      FormBuilderValidators. password(8),
      (val) {
        if (val != _formKey.currentState?.getRawValue("password")) {
          return "Password not match";
        }
        return null;
      }
    ]),
  ),

hungtrn75 avatar Mar 04 '25 15:03 hungtrn75