Bloc

2020-01-24  本文已影响0人  shz_Minato

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 状态。

上一篇下一篇

猜你喜欢

热点阅读