Bloc
Bloc:Business Logic Component,该库的目的:将表现层和逻辑层分离,并且让状态更加可以 预料。那么如何让状态 变得可以预料呢?首先,当状态有了 改变时,为这种改变作出某些 调整。其次,收缩状态改变的入口,只有一种方法 可以让状态 发生变化。
Bloc的核心概念
假设我们有一个计数器的应用,该应用有两个按钮:+、-按钮,分别用来加一和减一。
Events----事件
事件是Bloc的输入。
事件的来源有:用户的交互、页面的生命周期等。这些都可以产生事件。
比如:用户点击了加一或者减一按钮,那么我们需要把 某些事情 通知给应用中枢,应用中枢来响应 用户的点击(输入)。 这个点击 就是 事件产生的地方。 某些事情就是 我们定义的事件。
enum CounterEvent { increment, decrement }
在这里我们使用枚举定义了两个事件,可能在某些其他需要携带 额外信息的场景中,我们需要使用class
。
States----状态
状态是bloc的输出,表现 我们应用某一部分的状态。UI组件可以根据当前的状态全部或者部分的重绘。
我们在上面定义了加一和减一事件:CounterEvent.increment、CounterEvent.decrement。 现在我们需要表现出应用的状态,由于我们是计数器应用,所以应用的状态非常简单----数值,表现当前应用的数字是什么。
Transitions----转场
转场就是 一个状态到另一个状态的 改变,类似于我们动画的差值器过度。它由当前状态、事件、下一个状态组成。
比如:当用户点击加一或者减一按钮了,会触发CounterEvent事件,这些事件会 更新应用的状态(数值)。那么一个状态的改变 就是 一个转场。
下面就是 一个加一按钮事件的 转场
{
"currentState": 0,
"event": "CounterEvent.increment",
"nextState": 1
}
Streams----流
Dart中流的概念:一个异步数据的序列。下面我们看一下Java中流的概念:一个支持 串行和并行 操作的 元素序列,是不是很像。
Bloc是基于RxDart的,但是抽象了所有的Rx的特定实现细节。
流可以看成是一个水管,异步数据就是 水。流的相关概念可以在这里看
首先我们创建一个数据流:
Stream<int> countStream(int max) async* {
for (int i = 0; i < max; i++) {
yield i;
}
}
//流中元素为 0---max的int值
然后我们可以将流中数据求和:
Future<int> sumStream(Stream<int> stream) async {
int sum = 0;
await for (int value in stream) {
sum += value;
}
return sum;
}
最后我们可以看一下流的使用方式:
void main() async {
/// Initialize a stream of integers 0-9
Stream<int> stream = countStream(10);
/// Compute the sum of the stream of integers
int sum = await sumStream(stream);
/// Print the sum
print(sum); // 45
}
Blocs----Business Logic Component
Bloc是一个组件:将一个事件的输入流 转为 一个状态的输出流。所谓的转就是:bloc将一个流 转换 为另一个流。旧的流中元素是 事件,新的流中的元素是状态。
我们自定义的Bloc需要继承自核心包中的Bloc。比如:
import 'package:bloc/bloc.dart';
//接受两个范型:输入流的元素类型、输出流的元素类型
class CounterBloc extends Bloc<CounterEvent, int> {
}
初次之外,每一个Bloc都有一个初始的状态。初始状态就是 任何事件 未发生之前的 状态。比如 我们可以将我们的计数器的 初始状态 设置为 0.
class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;
}
并且,Bloc的作用是 将一个流 转为 另一个流,因为需要我们指定转换的方法。这个转换的方法就是父类抽象的mapEventToState方法。参数的范型就是输入流的类型,返回值的范型就是输出流的类型。 这里我们需要指定接收到 加一 和 减一类型时,输出流的元素应该是什么。
class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
//减一 -----》状态流的元素为 当前状态-1
yield state - 1;
break;
case CounterEvent.increment:
//加一 ----》状态流的元素为 当前状态+1
yield state + 1;
}
}
}
以上就是我们一个完成的Bloc,根据事件流的元素 去 生成状态流的元素。那么我们需要:如何告诉Bloc事件产生了呢?
Bloc基类中有一个add方法,该方法的作用就是:发送事件并触发mapEventToState。因此:我们需要在事件产生的地方 调用该方法,并把事件发送出去。比如:用户交互的地方,Bloc内部等。
现在我们手动调用add方法:
void main() {
//声明bloc
CounterBloc bloc = CounterBloc();
for (int i = 0; i < 3; i++) {
//模拟事件产生 并添加
bloc.add(CounterEvent.increment);
}
}
我们并没有看到什么输出,因为我们并没有做任何事情。我们可以重写onTransition方法,去追踪转场过程。
class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield state - 1;
break;
case CounterEvent.increment:
yield state + 1;
}
}
@override
void onTransition(Transition<CounterEvent, int> transition) {
// TODO: implement onTransition
super.onTransition(transition);
print(transition);
}
}
在执行后我们可以发现:
打印了
Transition { currentState: 0, event: CounterEvent.increment, nextState: 1 }
Transition { currentState: 1, event: CounterEvent.increment, nextState: 2 }
Transition { currentState: 2, event: CounterEvent.increment, nextState: 3 }
每一个转场包括了:当前的状态、本次事件、下个状态。
总结
以上就是Bloc的一些核心的概念,Bloc的基础还是流,它所做的事情就是转换:事件 map to 状态。
网友评论