The original

Medium.com/theotherdev…

preface

First, since this is not a basic tutorial, we take it for granted that this is a roadmap, and we also include a little 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 (Cubit) for tutorial purposes, so you’ll see the difference between the two.

Code, you can read the code first, then look at the documentation

Github.com/Alessandro-…

reference

  • Pub. Flutter – IO. Cn/packages/eq…
  • Pub. Flutter – IO. Cn/packages/va…
  • Pub. Flutter – IO. Cn/packages/fl…
  • Pub. Flutter – IO. Cn/packages/fo…

The body of the

start

Before we begin, let’s add some necessary packages in pubspec.yaml:

Equatable: ^2.0.0 flutter_bloc: ^7.0.0 formz: ^0.3.2Copy the code

Adding the Equatable package will only make your job easier, but if you want to compare instances of classes manually, just override “==” and hashCode.

The login status

Let’s start with a class that contains form state 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, ); }}Copy the code

Now let’s create our LoginCubit, which will take care of executing the logic, such as retrieving email and printing new status via 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())); }}}Copy the code

But how do we connect wrist ruler to our user interface? Here’s a relief for BlocProvider, a widget that provides a block for its children using blocProvider.of

(context)

BlocProvider(
  create: (_) => LoginCubit(),
  child: LoginForm(),
),
Copy the code

Login form

Now that it seems to be all in its own place, it’s time to tackle our final piece of puzzle, the entire 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(), ], ) ); }}Copy the code

To react to the new state emitted by Cubit, we need to wrap our form in a BlocConsumer, and now we’ll 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 display the UI response state changes to our Cubit.

The user interface

Our user interface consists of a column and five child elements, but we only show two short widgets:

class _EmailInputField extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    returnBlocBuilder<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) {
    returnBlocBuilder<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); }); }}Copy the code

Both widgets are wrapped in a BlocBuilder that takes care of rebuilding the widgets only when the cubit emits a new state for their respective evaluation properties, so if the user doesn’t type anything into the email field, for example, The EmailInputField will never be rebuilt.

Instead, if all fields are validated, the button calls the logInWithCredentials() function, which issues a new state (failed or succeeded) based on the API response.

Old tie remember to like and forward, I will be more motivated to present Flutter good article ~~~~


The elder brother of the © cat

ducafecat.tech/

github.com/ducafecat

The issue of

Open source

GetX Quick Start

Github.com/ducafecat/g…

News client

Github.com/ducafecat/f…

Strapi manual translation

getstrapi.cn

Wechat discussion group Ducafecat

A series of collections

The translation

Ducafecat. Tech/categories /…

Dart programming language basics

Space.bilibili.com/404904528/c…

Start Flutter with zero basics

Space.bilibili.com/404904528/c…

Flutter combat news client from scratch

Space.bilibili.com/404904528/c…

Flutter component development

Space.bilibili.com/404904528/c…

Flutter Bloc

Space.bilibili.com/404904528/c…

Flutter Getx4

Space.bilibili.com/404904528/c…

Docker Yapi

Space.bilibili.com/404904528/c…