前言
官方Github地址:https://felangel.github.io/bloc/#/
在理解BLoC之前,最好先对 Stream 有基础了解。
Bloc的目的是将展示层与逻辑层拆分开,使flutter代码层次更清晰,易于测试和复用。
参考
- https://blog.csdn.net/yumi0629/article/details/82759447
- https://www.didierboelens.com/2018/08/reactive-programming—-streams—-bloc/
架构
Bloc的设计秉承了三个核心价值:
- 简洁
- 强大
- 可测试
数据层(Data Layer)
数据层用于从一个或多个数据源获取或操作数据,数据层可以被分为两部分:
- Data Provider
- Repository
这一层,是应用的最底层,负责数据库交互、网络请求和其他的异步数据源操作。
Data Provider
Data Provider负责提供原始数据(raw data)。数据提供层通常会暴露简单的API执行CRUD操作。
class DataProvider {
Future<RawData> readData() async {
// Read from DB or make network request etc...
}
}
Repository
Repository层是一个或多个Data Provider与Bloc层交互的包裹器。
class Repository {
final DataProviderA dataProviderA;
final DataProviderB dataProviderB;
Future<Data> getAllDataThatMeetsRequirements() async {
final RawDataA dataSetA = await dataProviderA.readData();
final RawDataB dataSetB = await dataProviderB.readData();
final Data filteredData = _filterData(dataSetA, dataSetB);
return filteredData;
}
}
如上面代码所示,repository layer可以与多个data providers交互,并在将结果提交给Business Logic层之前对数据进行转换。
逻辑层(Bloc Layer)
Bloc层主要职责是根据展示层输入的events反馈新的state。可以把Bloc层想象为一个介于UI层(Presentation Layer)和数据层之间的桥梁。bloc层获取由用户输入产生的evnets并与repository交互并生成新的,可以被展示层消费的新的state。
class BusinessLogicComponent extends Bloc {
final Repository repository;
Stream mapEventToState(event) async* {
if (event is AppStarted) {
yield await repository.getAllDataThatMeetsRequirements();
}
}
}
逻辑层之间的通讯
Every bloc has a state stream which other blocs can subscribe to in order to react to changes within the bloc.
Blocs 可以依赖其他的 blocs 来响应自身状态的改变。
class MyBloc extends Bloc {
final OtherBloc otherBloc;
StreamSubscription otherBlocSubscription;
MyBloc(this.otherBloc) {
otherBlocSubscription = otherBloc.state.listen((state) {
// React to state changes here.
// Dispatch events here to trigger changes in MyBloc.
});
}
@override
void dispose() {
otherBlocSubscription.cancel();
super.dispose();
}
}
展示层(Presentation Layer)
展示层负责根据bloc的状态渲染自身,同时,它也负责管理用户的输入及应用对应event的生命周期。
class PresentationComponent {
final Bloc bloc;
PresentationComponent() {
bloc.dispatch(AppStarted());
}
build() {
// render UI based on bloc state
}
}
核心理念
Events(事件)
Events are the input to a Bloc. They are commonly dispatched in response to user interactions such as button presses or lifecycle events like page loads.
事件是BLoC的输入,他们通常被(例如:button点击、页面加载这样的)用户操作所触发。
States(状态)
States are the output of a Bloc and represent a part of your application’s state. UI components can be notified of states and redraw portions of themselves based on the current state.
Transitions(转变)
当从一个状态(state)变成另外一种状态的这种改变称为Transition。一个Transition包含三部分:
- 当前状态 (current state)
- 触发状态改变的事件 (event)
- 下一个状态 (next state)
Streams(流)
Stream代表一系列的异步数据。
A stream is a sequence of asynchronous data.
Blocs(业务逻辑组件)
每个Bloc必须继承至bloc package的基础 Bloc 类。
import 'package:bloc/bloc.dart';
class CounterBloc extends Bloc<CounterEvent, int> {
// add codes here ...
@override
int get initialState => 0;
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield currentState - 1;
break;
case CounterEvent.increment:
yield currentState + 1;
break;
}
}
}
- 每个Bloc必须定义一个初始 state。
- 每个Bloc必须实现基类的 mapEventToState 函数,这个函数将event作为输入的参数,且必须返回一个Stream类型的新的state。
- 可以在任何时机通过currentState属性访问当前的bloc state值。
- 每个Bloc(Business Logic Component)都有dispatch 方法,该方法通过event触发执行 mapEventToState函数。
onTransition
通过复写onTransition
方法,可以在Bolc的state发生改变之前获取状态信息。Tip:
onTransition
is a great place to add bloc-specific logging/analytics
onError
通过复写 onError
方法,可以获取Bloc中产生的异常。
Tip:
onError
is a great place to add bloc-specific error handling.
BlocDelegate(Bloc委托)
使用Bloc的另外一个附带好处是,可以在同一个地方访问所有的 Transitions。如果希望响应所有的Transitions,可以创建一个BlocDelegate。
Flutter BLoC
BlocBuilder
BlocBuilder 是一个实现了 Bolc
和 builder
函数的 Flutter widget。BlocBuilder 与 StreamBuilder 很相似,但其拥有更为简洁的API,减少了大量模版代码的编写。
BlocProvider
BlocProvider 是能够通过 BlocProvider.of<T>(context)
向子组件提供bloc 的Flutter widget。BlocProvider 被当作注入组件使用。因此,单例的bloc可以被多个子组件共享。