原文

https://medium.com/theotherde...

前言

首先,因为这不是一个根本的教程,咱们天经地义地认为这是一个路线的常识,咱们也蕴含了一点点的 validationformoz 包来创立可重用的模型; 这不是本教程的目标,以显示这将如何工作,您将看到这在下一个教程。对于登录局部,出于教程的目标,咱们还应用了 BLoC (Cubit) 的子集,因而您将看到这两者之间的区别。

代码,能够先浏览代码,再看文档

https://github.com/Alessandro...

参考

  • https://pub.flutter-io.cn/pac...
  • https://pub.flutter-io.cn/pac...
  • https://pub.flutter-io.cn/pac...
  • https://pub.flutter-io.cn/pac...

注释

开始

在咱们开始之前,让咱们在 pubspec.yaml 中增加一些必要的包:

equatable: ^2.0.0flutter_bloc: ^7.0.0formz: ^0.3.2

增加 equatable 包只会使您的工作更加容易,然而如果您想手动比拟类的实例,只须要重写 "==" 和 hashCode。

登录状态

让咱们从一个蕴含表单状态和所有字段状态的类开始:

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,    );  }}

当初让咱们创立咱们的 LoginCubit,它将负责执行逻辑,例如通过 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()));    }  }}

然而咱们如何将腕尺与咱们的用户界面连接起来呢?上面是对 BlocProvider 的拯救,这是一个小部件,它应用: BlocProvider.of<logincubit>(context) 为其子部件提供一个区块

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

登入表格

既然当初仿佛都在他本人的中央,是时候解决咱们的最初一块 puzzle,整个用户界面

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(),          ],        )    );  }}

为了对 Cubit 收回的新状态做出反馈,咱们须要将咱们的表单包裹在一个 BlocConsumer 中,当初咱们将裸露一个监听者和一个建造者。

  • Listener

这里咱们将监听状态更改,例如,在响应 API 调用时显示谬误或执行导航。

  • Builder

在这里,咱们将显示 ui 反馈状态的变动,咱们的 Cubit

用户界面

咱们的用户界面由一个列和 5 个子元素组成,然而咱们只展现 2 个简短的小部件:

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        );      },    );  }}

这两个小部件都包装在一个 BlocBuilder 中,只有当肘位为它们各自的评估属性收回新的状态时,BlocBuilder 才负责从新构建这些小部件,因而,例如,如果用户没有在 email 字段中键入任何内容,EmailInputField 将永远不会被从新构建。

相同,如果所有字段都通过验证,按钮将调用 logInWithCredentials() 函数,该函数将依据 API 响应收回一个新状态(失败或胜利)。

老铁记得 点赞、转发 ,我将更有能源出现 Flutter 好文~~~~


© 猫哥

https://ducafecat.tech/

https://github.com/ducafecat

往期

开源

GetX Quick Start

https://github.com/ducafecat/...

新闻客户端

https://github.com/ducafecat/...

strapi 手册译文

https://getstrapi.cn

微信探讨群 ducafecat

系列汇合

译文

https://ducafecat.tech/catego...

Dart 编程语言根底

https://space.bilibili.com/40...

Flutter 零根底入门

https://space.bilibili.com/40...

Flutter 实战从零开始 新闻客户端

https://space.bilibili.com/40...

Flutter 组件开发

https://space.bilibili.com/40...

Flutter Bloc

https://space.bilibili.com/40...

Flutter Getx4

https://space.bilibili.com/40...

Docker Yapi

https://space.bilibili.com/40...