[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-xsljaswk-1620260140025)( https://ducafecat.tech/2021/05/06/translation/mastering-flutter-bloc-pattern-for-login-part-1/2021-05-06-08-01-18.png )]
original text
https://medium.com/theotherdev-s/mastering-flutter-bloc-pattern-for-login-part-1-94082e139725
preface
First of all, since this is not a basic tutorial, we take it for granted that this is a route knowledge. We also include a little bit of validation and formoz packages to create reusable models; This is not the purpose of this tutorial to show how this will work, as you will see in the next tutorial. For the login section, we also used a subset of bloc (cube) for tutorial purposes, so you will see the difference between the two.
Code, you can read the code first and then read the document
https://github.com/Alessandro-v/bloc_login
reference resources
- https://pub.flutter-io.cn/packages/equatable
- https://pub.flutter-io.cn/packages/validation
- https://pub.flutter-io.cn/packages/flutter_bloc
- https://pub.flutter-io.cn/packages/formoz
text
start
Before we start, let's go to pubspec Add some necessary packages to yaml:
equatable: ^2.0.0 flutter_bloc: ^7.0.0 formz: ^0.3.2
Adding an equatable package will only make your work easier, but if you want to manually compare instances of classes, you just need to rewrite "= =" and hashCode.
Login status
Let's start with a class that contains form states and all field states:
class LoginState extends Equatable { const LoginState({ this.email = const Email.pure(), this.password = const Password.pure(), this.status = FormzStatus.pure, this.exceptionError, }); final Email email; final Password password; final FormzStatus status; final String exceptionError; @override List<Object> get props => [email, password, status, exceptionError]; LoginState copyWith({ Email email, Password password, FormzStatus status, String error, }) { return LoginState( email: email ?? this.email, password: password ?? this.password, status: status ?? this.status, exceptionError: error ?? this.exceptionError, ); } }
Now let's create our LoginCubit, which will be responsible for executing logic, such as getting email and outputting new status through emit:
class LoginCubit extends Cubit<LoginState> { LoginCubit() : super(const LoginState()); void emailChanged(String value) { final email = Email.dirty(value); emit(state.copyWith( email: email, status: Formz.validate([ email, state.password ]), )); } void passwordChanged(String value) { final password = Password.dirty(value); emit(state.copyWith( password: password, status: Formz.validate([ state.email, password ]), )); } Future<void> logInWithCredentials() async { if (!state.status.isValidated) return; emit(state.copyWith(status: FormzStatus.submissionInProgress)); try { await Future.delayed(Duration(milliseconds: 500)); emit(state.copyWith(status: FormzStatus.submissionSuccess)); } on Exception catch (e) { emit(state.copyWith(status: FormzStatus.submissionFailure, error: e.toString())); } } }
But how do we connect the wrist ruler to our user interface? The following is the rescue of BlocProvider. This is a widget that uses: BlocProvider Of < logincubit > (context) provides a block for its subcomponents
BlocProvider( create: (_) => LoginCubit(), child: LoginForm(), ),
Login form
Now that it seems to be in his own place, it's time to solve our last puzzle, the whole user interface
class LoginForm extends StatelessWidget { const LoginForm({Key key}) : super(key: key); @override Widget build(BuildContext context) { return BlocConsumer<LoginCubit, LoginState>( listener: (context, state) { if (state.status.isSubmissionFailure) { print('submission failure'); } else if (state.status.isSubmissionSuccess) { print('success'); } }, builder: (context, state) => Stack( children: [ Positioned.fill( child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(38.0, 0, 38.0, 8.0), child: Container( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.start, children: [ _WelcomeText(), _EmailInputField(), _PasswordInputField(), _LoginButton(), _SignUpButton(), ], ), ), ), ), state.status.isSubmissionInProgress ? Positioned( child: Align( alignment: Alignment.center, child: CircularProgressIndicator(), ), ) : Container(), ], ) ); } }
In order to respond to the new state sent by Cubit, we need to wrap our form in a BlocConsumer. Now we will expose a listener and a builder.
- Listener
Here we will listen for state changes, such as displaying errors or performing navigation in response to API calls.
- Builder
Here, we will show the change of ui reaction state, our Cubit.
user interface
Our user interface consists of a column and five sub elements, but we only show two short widgets:
class _EmailInputField extends StatelessWidget { @override Widget build(BuildContext context) { return BlocBuilder<LoginCubit, LoginState>( buildWhen: (previous, current) => previous.email != current.email, builder: (context, state) { return AuthTextField( hint: 'Email', key: const Key('loginForm_emailInput_textField'), keyboardType: TextInputType.emailAddress, error: state.email.error.name, onChanged: (email) => context .read<LoginCubit>() .emailChanged(email), ); }, ); } } class _LoginButton extends StatelessWidget { const _LoginButton({Key key}) : super(key: key); @override Widget build(BuildContext context) { return BlocBuilder<LoginCubit, LoginState>( buildWhen: (previous, current) => previous.status != current.status, builder: (context, state) { return CupertinoButton( child: Text('Login'), onPressed: state.status.isValidated ? () => context.read<LoginCubit>().logInWithCredentials() : null ); }, ); } }
Both widgets are packaged in a BlocBuilder, which is responsible for rebuilding them only when the elbow sends a new status for their respective evaluation properties, so, for example, if the user doesn't type anything in the email field, the EmailInputField will never be rebuilt.
Conversely, if all fields are verified, the button will call the logInWithCredentials() function, which will issue a new status (failure or success) based on the API response.
The old fellow can remember the praise and forwarding, and I will be more motivated to present Flutter good Wen ~ ~.
© Cat brother
https://ducafecat.tech/
https://github.com/ducafecat
Previous period
Open Source
GetX Quick Start
https://github.com/ducafecat/getx_quick_start
News client
https://github.com/ducafecat/flutter_learn_news
Translation of strapi manual
https://getstrapi.cn
Wechat discussion group ducafecat
Series collection
translation
https://ducafecat.tech/categories/%E8%AF%91%E6%96%87/
Fundamentals of Dart programming language
https://space.bilibili.com/404904528/channel/detail?cid=111585
Introduction to Flutter zero Foundation
https://space.bilibili.com/404904528/channel/detail?cid=123470
Actual combat of Flutter starts from scratch news client
https://space.bilibili.com/404904528/channel/detail?cid=106755
Fluent component development
https://space.bilibili.com/404904528/channel/detail?cid=144262
Flutter Bloc
https://space.bilibili.com/404904528/channel/detail?cid=177519
Flutter Getx4
https://space.bilibili.com/404904528/channel/detail?cid=177514
Docker Yapi
https://space.bilibili.com/404904528/channel/detail?cid=130578