4

When I want to confirm my password, but i can not compare the first and last passwords.

Here my password class. It works fine, but confirmPassword section not.

class Password extends FormzInput<String, PasswordValidationError> {
  const Password.pure() : super.pure('');
  const Password.dirty([String value = '']) : super.dirty(value);

  static final _passwordRegExp = RegExp(r'^[A-Za-z\d@$!%*?&]{8,}$');

  @override
  PasswordValidationError? validator(String value) {
    if (value.isEmpty) {
      return PasswordValidationError.empty;
    }
    return _passwordRegExp.hasMatch(value)
        ? null
        : PasswordValidationError.invalid;
  }
}

You can ask what is the value in validator. Let me show you.

  void passwordChanged(String value) {
    final password = Password.dirty(value);
    emit(state.copyWith(
      password: password,
      status: Formz.validate(
          [state.name, state.email, password, state.confirmPassword]),
    ));
  }

This is my normal password field. Now let's look confirm section.

class ConfirmPassword
    extends FormzInput<String, ConfirmedPasswordValidationError> {
  const ConfirmPassword.pure() : super.pure('');
  const ConfirmPassword.dirty([String value = '']) : super.dirty(value);

  static final _confirmPasswordRegExp = RegExp(r'^[A-Za-z\d@$!%*?&]{8,}$');
  
  @override
  ConfirmedPasswordValidationError? validator(String value) {
    if (value.isEmpty) {
      return ConfirmedPasswordValidationError.empty;
    } else if (!_confirmPasswordRegExp.hasMatch(value)) {
      return ConfirmedPasswordValidationError.invalid;
    }
    return (password**I mentioned here** == value)
        ? null
        : ConfirmedPasswordValidationError.mismatch;
  }
}

and value in validator:

  void confirmPasswordChanged(String value) {
    final confirmPassword =
        ConfirmPassword.dirty(value);
    emit(state.copyWith(
      confirmPassword:
          state.password.valid ? confirmPassword : const ConfirmPassword.pure(),
      status: Formz.validate(
          [state.name, state.email, state.password, confirmPassword]),
    ));
  }

How can i reach the first (normal) value field in confirmPassword section? Or Is there any way to the use with formz?

alperefesahin
  • 604
  • 6
  • 22

2 Answers2

12

This is a validator

 import 'package:formz/formz.dart';

enum ConfirmedPasswordValidationError {
  invalid,
  mismatch,
}

class ConfirmedPassword extends FormzInput<String, ConfirmedPasswordValidationError> {
  final String password;

  const ConfirmedPassword.pure({
    this.password = ''
  }) : super.pure('');

  const ConfirmedPassword.dirty({
    required this.password,
    String value = ''
  }) : super.dirty(value);

  @override
  ConfirmedPasswordValidationError? validator(String value) {
    if (value.isEmpty) {
      return ConfirmedPasswordValidationError.invalid;
    }
    return password == value
        ? null
        : ConfirmedPasswordValidationError.mismatch;
  }
}

extension Explanation on ConfirmedPasswordValidationError {
  String? get name {
    switch(this) {
      case ConfirmedPasswordValidationError.mismatch:
        return 'passwords must match';
      default:
        return null;
    }
  }
}

state management func bloc

  void _passwordConfirmationChanged(PasswordConfirmationChangede,Emitter<SignUpFormState> emit){
    final confirmedPassword = ConfirmedPassword.dirty(
      password: state.password.value,
      value: e.passwordConfirmStr,
    );
    emit(state.copyWith(
      confirmedPassword: confirmedPassword,
      status: Formz.validate([
        state.email,
        state.password,
        confirmedPassword,
      ]),
    ));
  }

Widget class

class _ConfirmPasswordInput extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<SignUpFormBloc, SignUpFormState>(
      buildWhen: (previous, current) =>
          previous.password != current.password ||
          previous.confirmedPassword != current.confirmedPassword,
      builder: (context, state) {
        return TextField(
          key: const Key('signUpForm_confirmedPasswordInput_textField'),
          onChanged: (confirmPassword) => context.read<SignUpFormBloc>()
            .add(SignUpFormEvent.passwordConfirmationChanged(confirmPassword)),
          obscureText: true,
          decoration: InputDecoration(
            labelText: 'confirm password',
            helperText: '',
            errorText: state.confirmedPassword.invalid
                ? 'passwords do not match'
                : null,
          ),
        );
      },
    );
  }
}
Eng Mghase
  • 524
  • 6
  • 14
  • good job after a long time, it works – alperefesahin Jan 16 '22 at 00:08
  • 1
    You have created "extension Explanation on ConfirmedPasswordValidationError ". Could you please elaborate on how instead of using static errorText in the widget like the one above, we can use the descriptions within extensions for various kinds of errors, in the widget text field itself? – Praveen Bhusal Apr 06 '22 at 15:15
  • Every validator class have explanation extension on it. so you can pass message depend on type of validator – Eng Mghase Feb 28 '23 at 13:01
2

Eng Mghase answer is correct

I have added the update of the confirmed password state on password update :

void _onPasswordChanged(
      SignupPasswordChanged event,
      Emitter<SignupState> emit,
    ) {
      final password = Password.dirty(event.password);
      final confirmPassword = ConfirmedPassword.dirty(password: password.value, value: state.confirmPassword.value);
      emit(state.copyWith(
        password: password,
        confirmPassword: confirmPassword,
        status: Formz.validate([password, confirmPassword, state.username]),
    ));
  }
eternel7
  • 53
  • 7