Bloc 的名字比较新颖,这个状态管理框架的目的是将 UI 层和业务逻辑进行分离。Bloc 的复杂度处于 ScopedModel 和 Redux 之间,相较于 ScopedModel,Bloc 拥有分明的架构处于业务逻辑,相较于 Redux,Bloc 着重于业务逻辑的分解,使得整个框架对于开发来说简单实用。git
Bloc 分为三层:github
Presentation Layer 只与 Bloc Layer 交互,Data Laye 也只与 Bloc Layer 交互。Bloc Layer 做为重要一层,处于表现层和数据层之间,使得 UI 和数据经过 Bloc Layer 进行交互。bash
因而可知,Bloc 的架构和客户端主流的 MVC 和 MVP 架构比较类似,但也存在 Event 和 State 的概念一同构成响应式框架。网络
BlocProvider,一般作为 App 的根布局。BlocProvider 能够保存 Bloc,在其它页面经过BlocProvider.of<Bloc>(context)
获取 Bloc。架构
Event,用户操做 UI 后发出的事件,用于通知 Bloc 层事件发生。app
State,页面状态,可用于构建 UI。一般是 Bloc 将接收到的 Event 转化为 State。框架
Bloc 架构的核心是 Bloc 类,Bloc 类是一个抽象类,有一个 mapEventToState(event)
方法须要实现。mapEventToState(event)
顾名思义,就是将用户点击 View 时发出的 event 转化为构建 UI 所用的 State。另外,在 StatefulWidget 中使用 bloc 的话,在 widget dispose 时,要调用 bloc.dispose()
方法进行释放。async
这里以常见的获取列表选择列表为例子。一个页面用于展现选中项和跳转到列表,一个页面用于显示列表。ide
在 pubspec.yaml
文件中引入 flutter_bloc
第三方库支持 bloc 功能。布局
# 引入 bloc 第三方库
flutter_bloc: ^0.9.0
复制代码
这一步无关紧要,但使用插件会方便开发,不使用的话也没什么问题。
Bloc 官方提供了 VSCode 和 Android studio 的插件,方便生成 Bloc 框架用到的相关类。 下文以 Android studio 的插件为例。
好比 list 页面,该插件会生成相应的类
从生成的五个文件中也能够看到,list_bloc
负责承载业务逻辑,list_page
负责编写 UI 界面,list_event
和 list_state
分别是事件和状态,其中 list.dart
文件是用于导出前面四个文件的。
具体使用可见
在 main.dart
中,使用 BlocProvider 做为父布局包裹,用于传递须要的 bloc。Demo 中包含两个页面,一个是展现页面 ShowPage,一个是列表页面 ListPage。
上面讲到,Bloc 的核心功能在于 Bloc 类,对于展现页面 ShowPage,会有一个 ShowBloc 继续自 Bloc 类。因为展现页面 ShowPage 会和列表页面 ListPage 有数据的互动,因此这里将 ShowBloc 保存在 BlocProvider 中进行传递。
@override
Widget build(BuildContext context) {
return BlocProvider(
bloc: _showBloc,
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ShowPage()));
}
复制代码
① ShowEvent
列表的 item 点击后,须要发送一个 event 通知其它页面列表被选中,这里定义一个 SelectShowEvent 做为这种 event 通知。
class SelectShowEvent extends ShowEvent {
String selected;
SelectShowEvent(this.selected);
}
复制代码
② ShowState
State 用于表示一种界面状态,即一个 State 就对应一个界面。插件在一开始会生成一个默认状态,InitialShowState。咱们可使用 InitialShowState 来表明初始的界面。另外,咱们本身定义一种状态,SelectedShowState,表明选中列表后的 State。
@immutable
abstract class ShowState {}
class InitialShowState extends ShowState {}
class SelectedShowState extends ShowState {
String _selectedString = "";
String get selected => _selectedString;
SelectedShowState(this._selectedString);
}
复制代码
③ ShowBloc
Bloc 的主要职责是接收 Event,而后把 Event 转化为对应的 State。这里的 ShowBloc 继续自 Bloc,须要重写实现抽象方法 mapEventToState(event)
。在这个方法中,咱们判断传过来的 event 是否是 SelectShowEvent,是则拿到 SelectShowEvent 中的 selected 变量去构建 SelectedShowState。mapEventToState(event)
返回的是一个 Stream,咱们经过 yield 关键字去返回一个 SelectedShowState。
class ShowBloc extends Bloc<ShowEvent, ShowState> {
@override
ShowState get initialState => InitialShowState();
@override
Stream<ShowState> mapEventToState(
ShowEvent event,
) async* {
if (event is SelectShowEvent) {
yield SelectedShowState(event.selected);
}
}
}
复制代码
④ ShowPage
在 ShowPage 的界面上,咱们须要根据 showBloc 中是否有被选中的列表项目去展于页面,因此这里咱们先使用使用BlocProvider.of<ShowBloc>(context)
去拿到 showBloc,接着再用 BlocBuilder 根据 showBloc 构建界面。使用 BlocBuilder 的好处就是可让页面自动响应 showBloc 的变化而变化。
var showBloc = BlocProvider.of<ShowBloc>(context);
...
BlocBuilder(
bloc: showBloc,
builder: (context, state) {
if (state is SelectedShowState) {
return Text(state.selected);
}
return Text("");
}),
复制代码
① ListEvent
列表页面,咱们一开始须要从网络中拉取列表数据,因此定义一个 FetchListEvent 事件在进入页面时通知 ListBloc 去获取列表。
@immutable
abstract class ListEvent extends Equatable {
ListEvent([List props = const []]) : super(props);
}
class FetchListEvent extends ListEvent {}
复制代码
② ListState
InitialListState 是插件默认生成的初始状态,另外定义一个 FetchListState 表明获取列表完成的状态。
@immutable
abstract class ListState extends Equatable {
ListState([List props = const []]) : super(props);
}
class InitialListState extends ListState {}
class FetchListState extends ListState {
List<String> _list = [];
UnmodifiableListView<String> get list => UnmodifiableListView(_list);
FetchListState(this._list);
}
复制代码
③ ListBloc
在 ListBloc 中,进行从网络获取列表数据的业务。这里经过一个延时操做摸拟网络请求,最后用 yield 返回列表数据。
class ListBloc extends Bloc<ListEvent, ListState> {
@override
ListState get initialState => InitialListState();
@override
Stream<ListState> mapEventToState(
ListEvent event,
) async* {
if (event is FetchListEvent) {
// 模拟网络请求
await Future.delayed(Duration(milliseconds: 2000));
var list = [
"1. Bloc artitechture",
"2. Bloc artitechture",
"3. Bloc artitechture",
"4. Bloc artitechture",
"5. Bloc artitechture",
"6. Bloc artitechture",
"7. Bloc artitechture",
"8. Bloc artitechture",
"9. Bloc artitechture",
"10. Bloc artitechture"
];
yield FetchListState(list);
}
}
}
复制代码
④ ListPage
在列表页面初始化时有两个操做,一个是初始化 listBloc,一个是发出列表请求的 Event。
@override
void initState() {
bloc = ListBloc(); // 初始化listBloc
bloc.dispatch(FetchListEvent()); // 发出列表请求事件
super.initState();
}
复制代码
接下用,即是用 BlocBuilder 去响应状态。当 state 是 InitialListState,说明未获取列表,则显示 loading 界面,当 state 是 FetchListState 时,说明已经成功获取列表,显示列表界面。
body: BlocBuilder(
bloc: bloc,
builder: (context, state) {
// 根据状态显示界面
if (state is InitialListState) {
// 显示 loading 界面
return buildLoad();
} else if (state is FetchListState) {
// 显示列表界面
var list = state.list;
return buildList(list);
}
}));
复制代码
最后,记得对 bloc 进行 dispose()
。
@override
void dispose() {
bloc.dispose();
super.dispose();
}
复制代码
具体代码能够到 github 查看。
在 Bloc 的架构中,将一个页面和一个 Bloc 相结合,由页面产生 Event,Bloc 根据业务须要将 Event 转化为 State,再把 State 交给页面中的 BlocBuilder 构建 UI。Demo 中只是给出了简单的状态管理,实际项目中,好比网络请求,有请求中、请求成功、请求失败的多种状态,能够作适当封装使 Bloc 更加易用。相比于 Redux,Bloc 不须要将全部状态集中管理,这样对于不一样模块的页面易于拆分,对于代码量比较大的客户端而言,Bloc 的架构会相对比较友好。