form_bloc

form_bloc是结合bloc的表单库,运用bloc的特性实现UI和逻辑的分离,使得表单的逻辑更加清晰,代码更加简洁。

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';

void main() => runApp(const App());

class App extends StatelessWidget {
const App({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginForm(),
);
}
}

class LoginFormBloc extends FormBloc<String, String> {
// 不同的表单项对应不同的FieldBloc
final email = TextFieldBloc(
validators: [
FieldBlocValidators.required,
FieldBlocValidators.email,
],
);

final password = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);

final showSuccessResponse = BooleanFieldBloc();

LoginFormBloc() {
//添加表单项
addFieldBlocs(
fieldBlocs: [
email,
password,
showSuccessResponse,
],
);
}

/// 表单提交方法
@override
void onSubmitting() async {
debugPrint(email.value);
debugPrint(password.value);
debugPrint(showSuccessResponse.value.toString());

await Future<void>.delayed(const Duration(seconds: 1));

// 提交进度变化
emitSubmitting(progress: 0.2);
await Future<void>.delayed(Duration(milliseconds: 400));
emitSubmitting(progress: 0.6);
await Future<void>.delayed(Duration(milliseconds: 400));
emitSubmitting(progress: 1.0);
//取消提交
//emitSubmissionCancelled();

if (showSuccessResponse.value) {
emitSuccess(); //提交成功,发送成功消息,触发UI更新
} else {
emitFailure(failureResponse: 'This is an awesome error!'); //提交失败,发送失败消息,触发UI更新
}
}

@override
Future<void> close() {
// 释放所有表单项
email.close();
password.close();
showSuccessResponse.close();
return super.close();
}

}

class LoginForm extends StatelessWidget {
const LoginForm({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LoginFormBloc(),
child: Builder(
builder: (context) {
final loginFormBloc = context.read<LoginFormBloc>();

return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(title: const Text('Login')),
body: FormBlocListener<LoginFormBloc, String, String>(
onSubmitting: (context, state) {
LoadingDialog.show(context);
},
onSubmissionFailed: (context, state) {
LoadingDialog.hide(context);
},
onSuccess: (context, state) {
LoadingDialog.hide(context);

Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const SuccessScreen()));
},
onFailure: (context, state) {
LoadingDialog.hide(context);

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.failureResponse!)));
},
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: AutofillGroup(
child: Column(
children: <Widget>[
TextFieldBlocBuilder(
textFieldBloc: loginFormBloc.email,
keyboardType: TextInputType.emailAddress,
autofillHints: const [
AutofillHints.username,
],
decoration: const InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
),
TextFieldBlocBuilder(
textFieldBloc: loginFormBloc.password,
suffixButton: SuffixButton.obscureText,
autofillHints: const [AutofillHints.password],
decoration: const InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
),
SizedBox(
width: 250,
child: CheckboxFieldBlocBuilder(
booleanFieldBloc: loginFormBloc.showSuccessResponse,
body: Container(
alignment: Alignment.centerLeft,
child: const Text('Show success response'),
),
),
),
ElevatedButton(
onPressed: loginFormBloc.submit,
child: const Text('LOGIN'),
),
],
),
),
),
),
);
},
),
);
}
}

class LoadingDialog extends StatelessWidget {
static void show(BuildContext context, {Key? key}) => showDialog<void>(
context: context,
useRootNavigator: false,
barrierDismissible: false,
builder: (_) => LoadingDialog(key: key),
).then((_) => FocusScope.of(context).requestFocus(FocusNode()));

static void hide(BuildContext context) => Navigator.pop(context);

const LoadingDialog({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Card(
child: Container(
width: 80,
height: 80,
padding: const EdgeInsets.all(12.0),
child: const CircularProgressIndicator(),
),
),
),
);
}
}

class SuccessScreen extends StatelessWidget {
const SuccessScreen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(Icons.tag_faces, size: 100),
const SizedBox(height: 10),
const Text(
'Success',
style: TextStyle(fontSize: 54, color: Colors.black),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
ElevatedButton.icon(
onPressed: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const LoginForm())),
icon: const Icon(Icons.replay),
label: const Text('AGAIN'),
),
],
),
),
);
}
}


表单项类型

Read More

ReactiveX

Observables

概述

ReactiveX是Reactive Extensions的缩写,一般简写为Rx,最初是LINQ的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源,Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,Rx库支持大部分主流语
言。

使用这种方法的优点是,当你有一大堆的任务是不相互依赖,你就可以同时执行他们,而不是等待每一个类启动下一个前完成,这样你的整个任务包只需要花最长的任务时间。

在ReactiveX中,一个观察者(Observer)订阅一个可观察对象(Observable)。观察者对Observable发射的数据或数据序列作出响应。这种模式可以极大地简化并发操作,因为它创建了一个处于待命状态的观察者哨兵,在未来某个时刻响应Observable的通知,不需要阻塞等待Observable发射数据。

背景知识

在很多软件编程任务中,或多或少你都会期望你写的代码能按照编写的顺序,一次一个的顺序执行和完成。但是在ReactiveX中,很多指令可能是并行执行的,之后他们的执行结果才会被观察者捕获,顺序是不确定的。为达到这个目的,你定义一种获取和变换数据的机制,而不是调用一个方法。在这种机制下,存在一个可观察对象(Observable),观察者(Observer)订阅(Subscribe)它,当数据就绪时,之前定义的机制就会分发数据给一直处于等待状态的观察者哨兵。

Read More

flutter_bloc

基本用法

核心概念:将UI和数据分离,数据使用Event、Bloc(Cubit)、State来进行单项流转。

  1. 通过BlocProvider提供Bloc供不同地方使用
  2. 使用BlocBuilder监听Bloc的状态变化,根据状态变化来刷新UI
Read More