原文
https://medium.com/theotherde…
前言
首先,因为这不是一个根本的教程,咱们天经地义地认为这是一个路线的常识,咱们也蕴含了一点点的 validation
与 formoz
包来创立可重用的模型; 这不是本教程的目标,以显示这将如何工作,您将看到这在下一个教程。对于登录局部,出于教程的目标,咱们还应用了 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.0
flutter_bloc: ^7.0.0
formz: ^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…
发表回复