Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate form when submit in flutter with flutter_bloc?

This is my change password screen. I was using flutter_bloc for implementing mvvc pattern. This page works fine with bloc. But what I am trying to achieve is validate form when submitting the form. As I was new to flutter I have no idea how to do this.

Change Password Event

abstract class ChangePasswordEvent extends Equatable {
  const ChangePasswordEvent();
}

class SubmitButtonPressed extends ChangePasswordEvent {
  final String oldPassword;
  final String newPassword;

  const SubmitButtonPressed({@required this.oldPassword, this.newPassword});

  @override
  List<Object> get props => [oldPassword, newPassword];
}

Change Password State

abstract class ChangePasswordState extends Equatable {
  const ChangePasswordState();

  @override
  List<Object> get props => [];
}

class ChangePasswordInitial extends ChangePasswordState {}

class ChangePasswordLoading extends ChangePasswordState {}

class ChangePasswordSuccess extends ChangePasswordState {}

class ChangePasswordFailure extends ChangePasswordState {
  final String error;

  const ChangePasswordFailure({@required this.error});

  @override
  List<Object> get props => [error];

  @override
  String toString() => 'ChangePasswordFailure { error: $error }';
}

Change Password Bloc

class ChangePasswordBloc
    extends Bloc<ChangePasswordEvent, ChangePasswordState> {
  final UserRepository userRepository;

  ChangePasswordBloc({
    @required this.userRepository,
  }) : assert(userRepository != null);

  @override
  ChangePasswordState get initialState => ChangePasswordInitial();

  @override
  Stream<ChangePasswordState> mapEventToState(
      ChangePasswordEvent event) async* {
    if (event is SubmitButtonPressed) {
      yield ChangePasswordLoading();

      try {
        final bool isPasswordChanged = await userRepository.changePassword(
          event.oldPassword,
          event.newPassword,
        );

        if (isPasswordChanged) {
          yield ChangePasswordSuccess();
        }
      } catch (error) {
        yield ChangePasswordFailure(error: error);
      }
    }
  }
}

Change Password Page

class ChangePasswordPage extends StatelessWidget {
  final UserRepository userRepository;

  ChangePasswordPage({Key key, @required this.userRepository})
      : assert(userRepository != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Change Password'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: BlocProvider(
          create: (context) {
            return ChangePasswordBloc(
              userRepository: userRepository,
            );
          },
          child: ChangePasswordForm(),
        ),
      ),
    );
  }
}

Change Password Form

class ChangePasswordForm extends StatefulWidget {
  @override
  _ChangePasswordFormState createState() => _ChangePasswordFormState();
}

class _ChangePasswordFormState extends State<ChangePasswordForm> {
  final userRepository = UserRepository();
  final _formKey = GlobalKey<FormState>();

  final _oldPassController = TextEditingController();
  final _newPassController = TextEditingController();
  final _confirmPassController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    _onSubmitButtonPressed() {
      BlocProvider.of<ChangePasswordBloc>(context).add(
        SubmitButtonPressed(
          oldPassword: _oldPassController.text,
          newPassword: _newPassController.text,
        ),
      );
    }

    return BlocListener<ChangePasswordBloc, ChangePasswordState>(
      listener: (context, state) {
        if (state is ChangePasswordFailure) {
          Scaffold.of(context).showSnackBar(
            SnackBar(
              content: Text('${state.error}'),
              backgroundColor: Colors.red,
            ),
          );
        }

        if (state is ChangePasswordSuccess) {
          Scaffold.of(context).showSnackBar(
            SnackBar(
              content: Text('Password Changed Successfully'),
              backgroundColor: Colors.green,
            ),
          );
        }
      },
      child: BlocBuilder<ChangePasswordBloc, ChangePasswordState>(
        builder: (context, state) {
          return Form(
            key: _formKey,
            child: Column(
              children: [
                TextFormField(
                  decoration: InputDecoration(labelText: 'Old Password'),
                  controller: _oldPassController,
                ),
                SizedBox(height: 20.0),
                TextFormField(
                  decoration: InputDecoration(labelText: 'New Password'),
                  controller: _newPassController,
                  obscureText: true,
                ),
                SizedBox(height: 20.0),
                TextFormField(
                  decoration: InputDecoration(labelText: 'Confirm Password'),
                  controller: _confirmPassController,
                  obscureText: true,
                  validator: (value) {
                    final String _newPassword = _newPassController.text;

                    if (_newPassword != value) {
                      return "Password Mismatch";
                    }

                    return null;
                  },
                ),
                SizedBox(height: 20.0),
                RaisedButton(
                  onPressed: () {
                    if (state is! ChangePasswordLoading) {
                      final form = _formKey.currentState;

                      if (form.validate()) {
                        return _onSubmitButtonPressed();
                      }

                      return null;
                    }
                  },
                  child: Text('Submit'),
                ),
              ],
            ),
          );
        },
      ),
    );
  }
}
like image 646
Dheepak S Avatar asked Feb 18 '20 09:02

Dheepak S


People also ask

How do you validate a Flutter form?

Setting up a form to validateStart by creating a new Flutter project in either of VS Code or Android Studio. Replace the Flutter default counter application in main. dart with your own stateful widget. The formKey handles the state of the form, validation, and saving.

How do you validate multiple TextFormField in Flutter?

Create a Form with GlobalKey To validate the user input, we need the Form widget. The Form widget serves as a container to validate multiple TextFormFields and needs a GlobalKey to uniquely identify the form. It is also used to get the current state of the form.


Video Answer


1 Answers

You go and see on their example at: Form Validation

The example validate the email & password format, you should change it accordingly. Your state should be something like:

class MyFormState extends Equatable {
  final String email;
  final bool isEmailValid;
  final String password;
  final bool isPasswordValid;
  final bool formSubmittedSuccessfully;

  bool get isFormValid => isEmailValid && isPasswordValid;
  //....
}

The BLoC to validate:

class MyFormBloc extends Bloc<MyFormEvent, MyFormState> {
  final RegExp _emailRegExp = RegExp(
    r'^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$',
  );
  final RegExp _passwordRegExp = RegExp(
    r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$',
  );

  @override
  MyFormState get initialState => MyFormState.initial();

  @override
  void onTransition(Transition<MyFormEvent, MyFormState> transition) {
    print(transition);
  }

  @override
  Stream<MyFormState> mapEventToState(
    MyFormEvent event,
  ) async* {
    if (event is EmailChanged) {
      yield state.copyWith(
        email: event.email,
        isEmailValid: _isEmailValid(event.email),
      );
    }
    if (event is PasswordChanged) {
      yield state.copyWith(
        password: event.password,
        isPasswordValid: _isPasswordValid(event.password),
      );
    }
    if (event is FormSubmitted) {
      yield state.copyWith(formSubmittedSuccessfully: true);
    }
    if (event is FormReset) {
      yield MyFormState.initial();
    }
  }

  bool _isEmailValid(String email) {
    return _emailRegExp.hasMatch(email);
  }

  bool _isPasswordValid(String password) {
    return _passwordRegExp.hasMatch(password);
  }
}

And the the Form build method:

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<MyFormBloc, MyFormState>(
      builder: (context, state) {
        if (state.formSubmittedSuccessfully) {
          return SuccessDialog(onDismissed: () {
            _emailController.clear();
            _passwordController.clear();
            _myFormBloc.add(FormReset());
          });
        }
        return Form(
          child: Column(
            children: <Widget>[
              TextFormField(
                controller: _emailController,
                decoration: InputDecoration(
                  icon: Icon(Icons.email),
                  labelText: 'Email',
                ),
                keyboardType: TextInputType.emailAddress,
                autovalidate: true,
                validator: (_) {
                  return state.isEmailValid ? null : 'Invalid Email';
                },
              ),
              TextFormField(
                controller: _passwordController,
                decoration: InputDecoration(
                  icon: Icon(Icons.lock),
                  labelText: 'Password',
                ),
                obscureText: true,
                autovalidate: true,
                validator: (_) {
                  return state.isPasswordValid ? null : 'Invalid Password';
                },
              ),
              RaisedButton(
                onPressed: state.isFormValid ? _onSubmitPressed : null,
                child: Text('Submit'),
              ),
            ],
          ),
        );
      },
    );
  }
like image 107
Thuong Avatar answered Nov 09 '22 03:11

Thuong