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'),
),
],
),
);
},
),
);
}
}
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.
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.
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'),
),
],
),
);
},
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With