flutter_form_bloc
在Flutter中创建精美的表单。预填充、异步验证、更新表单字段以及根据表单状态显示进度、失败、成功或导航的最简单方法。
使用form_bloc将表单状态和业务逻辑与用户界面分离。
在使用此包之前,您需要了解bloc包的核心概念和flutter_bloc的基础知识。
小部件
- TextFieldBlocBuilder:一个可以显示建议的Material Design文本字段。
- DropdownFieldBlocBuilder:一个Material Design下拉菜单。
- RadioButtonGroupFieldBlocBuilder:一个Material Design单选按钮。
- CheckboxFieldBlocBuilder:一个Material Design复选框。
- CheckboxGroupFieldBlocBuilder:一个Material Design复选框组。
- FormBlocListener:响应FormBloc状态变化的BlocListener。
笔记
FormBloc,InputFieldBloc,TextFieldBloc,BooleanFieldBloc,SelectFieldBloc,MultiSelectFieldBloc都是blocs,因此您可以使用BlocBuilder或BlocListener(来自flutter_bloc)来使任何您想要的widget与任何FieldBloc或FormBloc兼容。
如果您希望我添加其他小部件,请告诉我,或提交一个pr。
示例
- 具有异步验证的FieldBlocs:BLoC - UI。
- 手动设置FieldBloc错误:BLoC - UI。
- 带提交进度的FormBloc:BLoC - UI。
- 无自动验证的FormBloc:BLoC - UI。
- 复杂的异步预填充FormBloc:BLoC - UI。
- 还有更多示例.
基本示例
dependencies:
form_bloc: ^0.5.1
flutter_form_bloc: ^0.4.2
flutter_bloc: ^0.21.0
import 'package:form_bloc/form_bloc.dart';
class LoginFormBloc extends FormBloc<String, String> {
final emailField = TextFieldBloc(validators: [Validators.email]);
final passwordField = TextFieldBloc();
final UserRepository _userRepository;
LoginFormBloc(this._userRepository);
@override
List<FieldBloc> get fieldBlocs => [emailField, passwordField];
@override
Stream<FormBlocState<String, String>> onSubmitting() async* {
try {
_userRepository.login(
email: emailField.value,
password: passwordField.value,
);
yield currentState.toSuccess();
} catch (e) {
yield currentState.toFailure();
}
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:flutter_form_bloc_example/forms/simple_login_form_bloc.dart';
import 'package:flutter_form_bloc_example/widgets/widgets.dart';
class LoginForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider<LoginFormBloc>(
builder: (context) =>
LoginFormBloc(RepositoryProvider.of<UserRepository>(context)),
child: Builder(
builder: (context) {
final formBloc = BlocProvider.of<LoginFormBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Simple login')),
body: FormBlocListener<LoginFormBloc, String, String>(
onSubmitting: (context, state) => LoadingDialog.show(context),
onSuccess: (context, state) {
LoadingDialog.hide(context);
Navigator.of(context).pushReplacementNamed('success');
},
onFailure: (context, state) {
LoadingDialog.hide(context);
Notifications.showSnackBarWithError(
context, state.failureResponse);
},
child: ListView(
children: <Widget>[
TextFieldBlocBuilder(
textFieldBloc: formBloc.emailField,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
),
TextFieldBlocBuilder(
textFieldBloc: formBloc.passwordField,
suffixButton: SuffixButton.obscureText,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: formBloc.submit,
child: Center(child: Text('LOGIN')),
),
),
],
),
),
);
},
),
);
}
}
基本用法
1. 导入它
import 'package:form_bloc/form_bloc.dart';
2. 创建一个继承自 FormBloc<SuccessResponse, FailureResponse> 的类
FormBloc<SuccessResponse, FailureResponse>
SuccessResponse 成功响应的类型。
FailureResponse 失败响应的类型。
例如,LoginFormBloc 的 SuccessResponse 类型和 FailureResponse 类型将是 String。
import 'package:form_bloc/form_bloc.dart';
class LoginFormBloc extends FormBloc<String, String> {}
2. 创建 Field Blocs
您需要创建字段bloc,并且这些字段必须是final的。
您可以创建
- InputFieldBloc
<Value>. - TextFieldBloc.
- BooleanFieldBloc.
- SelectFieldBloc
<Value>. - MultiSelectFieldBloc
<Value>.
例如,LoginFormBloc 将有两个 TextFieldBloc。
import 'package:form_bloc/form_bloc.dart';
class LoginFormBloc extends FormBloc<String, String> {
final emailField = TextFieldBloc(validators: [Validators.email]);
final passwordField = TextFieldBloc();
}
3. 添加服务/存储库
在此示例中,我们需要一个UserRepository来进行登录。
import 'package:form_bloc/form_bloc.dart';
class LoginFormBloc extends FormBloc<String, String> {
final emailField = TextFieldBloc(validators: [Validators.email]);
final passwordField = TextFieldBloc();
final UserRepository _userRepository;
LoginFormBloc(this._userRepository);
}
4. 实现 get method fieldBlocs
您需要覆盖 fieldBlocs 方法,并返回一个包含所有FieldBlocs的列表。
例如,LoginFormBloc 需要返回一个包含emailField和passwordField的列表。
import 'package:form_bloc/form_bloc.dart';
class LoginFormBloc extends FormBloc<String, String> {
final emailField = TextFieldBloc(validators: [Validators.email]);
final passwordField = TextFieldBloc();
final UserRepository _userRepository;
LoginFormBloc(this._userRepository);
@override
List<FieldBloc> get fieldBlocs => [emailField, passwordField];
}
5. 实现 onSubmitting 方法
onSubmitting 返回一个Stream<FormBlocState<SuccessResponse, FailureResponse>>。
当您调用loginFormBloc.submit()且FormBlocState.isValid为true(即每个字段bloc都有有效值)时,将调用此方法。
您可以通过调用emailField.value或passwordField.value来获取每个字段bloc的当前value。
您应该在此处调用此表单的所有业务逻辑,并yield相应的状态。
您可以使用以下方式yield一个新状态:
- currentState.toFailure([FailureResponse failureResponse]).
- currentState.toSuccess([SuccessResponse successResponse]).
- currentState.toLoaded().
在此处查看其他状态:here。
例如,如果_userRepository.login方法未抛出任何异常,则LoginFormBloc的onSubmitting将返回一个Stream<FormBlocState<String, String>>并yield currentState.toSuccess();如果抛出异常,则yield currentState.toFailure()。
import 'package:form_bloc/form_bloc.dart';
class LoginFormBloc extends FormBloc<String, String> {
final emailField = TextFieldBloc(validators: [Validators.email]);
final passwordField = TextFieldBloc();
final UserRepository _userRepository;
LoginFormBloc(this._userRepository);
@override
List<FieldBloc> get fieldBlocs => [emailField, passwordField];
@override
Stream<FormBlocState<String, String>> onSubmitting() async* {
try {
_userRepository.login(
email: emailField.value,
password: passwordField.value,
);
yield currentState.toSuccess();
} catch (e) {
yield currentState.toFailure();
}
}
}
6. 创建一个表单Widget
您需要创建一个具有FormBloc访问权限的widget。
在这种情况下,我将使用BlocProvider来实现。
class LoginForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider<LoginFormBloc>(
builder: (context) =>
LoginFormBloc(RepositoryProvider.of<UserRepository>(context)),
child: Builder(
builder: (context) {
final formBloc = BlocProvider.of<LoginFormBloc>(context);
return Scaffold();
},
),
);
}
}
6. 添加FormBlocListener以管理表单状态更改
您需要添加一个FormBlocListener。
在这个例子中
- 当状态为loading时,我将显示一个加载对话框。
- 当状态为success时,我将隐藏对话框并导航到success屏幕。
- 当状态为failure时,我将隐藏对话框并显示一个包含错误的snackbar。
...
return Scaffold(
appBar: AppBar(title: Text('Simple login')),
body: FormBlocListener<LoginFormBloc, String, String>(
onSubmitting: (context, state) => LoadingDialog.show(context),
onSuccess: (context, state) {
LoadingDialog.hide(context);
Navigator.of(context).pushReplacementNamed('success');
},
onFailure: (context, state) {
LoadingDialog.hide(context);
Notifications.showSnackBarWithError(
context, state.failureResponse);
},
child:
),
),
);
...
6. 将 Field Blocs 与 Field Blocs Builder 连接
在这个例子中,我将使用TextFieldBlocBuilder连接LoginFormBloc的emailField和passwordField。
...
child: ListView(
children: <Widget>[
TextFieldBlocBuilder(
textFieldBloc: formBloc.emailField,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
),
TextFieldBlocBuilder(
textFieldBloc: formBloc.passwordField,
suffixButton: SuffixButton.obscureText,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
),
],
),
...
7. 添加一个用于提交FormBloc的Widget
在这个例子中,我将添加一个RaisedButton并将FormBloc的submit方法传递给它来提交表单。
...
child: ListView(
children: <Widget>[
...,
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: formBloc.submit,
child: Center(child: Text('LOGIN')),
),
),
],
),
...