目录
- 前言
- 两种使用模式
- Cubit模式
- 最后
前言
目前Flutter三大主流状态管理框架分别是provider、flutter_bloc、getx,三大状态管理框架各有优劣,本篇文章将介绍其中的flutter_bloc框架的使用,他是bloc设计思想模式在flutter上的实现,bloc全程全称 business logic ,业务逻辑的意思,核心思想就是最大程度的将页面ui与数据逻辑的解耦,使我们的项目可读性、可维护性、健壮性增强。
两种使用模式
首先第一步引入插件:
flutter_bloc: ^.1.1
引入之后,flutter_bloc使用支持以下两种模式管理。
Bloc模式,分别有ui层(view)、数据层(state)、事件层(event)、逻辑处理层(bloc),适合大型复杂页面使用。这四层结构bloc在源码处就进行了封装处理,所以我们使用的时候是必须要分离出来的,比如event和state是要强制分开去写的。这也导致了简单页面使用此模式复杂化的问题,所以这种模式对于简单页面是非常没有必要的,但是如果是复杂页面的话,非常建议使用此模式,相信你在处理页面数据逻辑的时候会非常的清晰。
下面我们以计数器为例写个demo,记住bloc模式有四层,且必须分开,我们建四个文件分别代表这四层,
数据层: 用来存放数据,这个很简单。
/// 数据层 | |
class DemoState { | |
// 自增数字 | |
late int num; | |
DemoState init() { | |
// 初始化为 | |
return DemoState()..num =; | |
} | |
DemoState clone() { | |
// 获取最新对象 | |
return DemoState()..num = num; | |
} | |
} |
事件层: 用来存放页面所有事件的地方,比如计数器页面只有初始化事件和自增事件。
/// 页面中所有的交互事件 | |
abstract class DemoEvent {} | |
/// 初始化事件 | |
class InitEvent extends DemoEvent {} | |
/// 自增事件 | |
class IncrementEvent extends DemoEvent {} |
Bloc逻辑处理层: 处理上方数据和事件逻辑的地方,通过源码就能发现作者的意图,泛型里必须分开传入事件和数据,也足以说明这个模式的特点,就是为复杂页面准备的。
所以如果写计数器的话,你就会感觉非常没有必要,因为计数器页面很简单,但是当你的state层里的数据非常多且复杂的时候,你就能体会出分开的好处了。
代码:
/// 逻辑处理层 继承Bloc | |
class DemoBloc extends Bloc<DemoEvent, DemoState> { | |
///构造方法 | |
DemoBloc() : super(DemoState().init()) { | |
/// on 注册所有事件 on固定写法 | |
on<InitEvent>(_init); | |
on<IncrementEvent>(_add); | |
} | |
/// 私有化逻辑方法 暴露Event事件即可 | |
void _init(InitEvent event, Emitter<DemoState> emit) { | |
// emit方法,通知更新状态 类似于 ChangeNotifier的notifyListeners方法。 | |
emit(state.clone()); | |
} | |
_add(IncrementEvent event, Emitter<DemoState> emit) { | |
state.num++; | |
// 调用emit方法更新状态 | |
emit(state.clone()); | |
} | |
} |
UI层: UI层只负责页面的编写,而无需关心数据的生成,根节点返回BlocProvider,并实现create方法,返回我们的bloc实体类。child实现我们的 UI页面。
/// UI层 | |
class BlocNumPage extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
// 以下固定写法 | |
return BlocProvider( | |
create: (BuildContext context) => DemoBloc()..add(InitEvent()), | |
child: Builder(builder: (context) => _buildPage(context)), | |
); | |
} | |
Widget _buildPage(BuildContext context) { | |
// 获取bloc实例 | |
final bloc = BlocProvider.of<DemoBloc>(context); | |
return Stack( | |
children: [ | |
Center( | |
// 对于需要更新状态的组件 外层包裹一个BlocBuilder,传入bloc、state | |
child: BlocBuilder<DemoBloc, DemoState>( | |
builder: (context, state) { | |
// 返回具体ui组件 | |
return Text("点击了${bloc.state.num}次"); | |
}, | |
), | |
), | |
Positioned( | |
bottom:, | |
right:, | |
child: FloatingActionButton( | |
onPressed: () { | |
// 调用add方法触发自增事件, | |
bloc.add(IncrementEvent()); | |
}, | |
child: Icon(Icons.add), | |
), | |
) | |
], | |
); | |
} | |
} |
效果:
Cubit模式
Cubit模式,分别有ui层(view)、数据层(state)、逻辑处理层(cubit),相较于bloc模式去掉了event层,适合比较简单的页面。跟bloc模式只是逻辑处理哪里发生了改变,数据层、页面ui层代码一模一样。
可以看到Cubit模式的逻辑就少了很多代码,而且是直接处理数据即可。通过源码,作者意图也很明显,只是传递了数据层。
/// 写逻辑 | |
class CubitCubit extends Cubit<CubitState> { | |
CubitCubit() : super(CubitState().init()); | |
///自增 | |
void increment() => emit(state.clone()..num = state.num +); | |
} |
其他写法跟Bloc是一样的,就不粘贴了,那看到这有的小伙伴可能就要问了,一个页面要创建3、4个文件,这也太麻烦了吧,高端的程序员往往不会去写一些重复性较高的代码,其实上面的四个文件都是可以通过插件自动生成的,这里下面两个插件,一个是官方的,官方的不会自动生成ui层的文件,一个是小呆呆写的,可以自动生成ui层重复性的代码文件,两者区别不大,推荐小呆呆的,因为可以多生成一个文件。
最后
Bloc本质上是一种数据逻辑和UI解耦思想,上面的演示只是非常非常简单的用法,就可以看出作者在源码层给我们强制性的设定了非常明确的各个模型,每个模型只专心负责一个事情,这样看起来一个页面会非常的清晰,可以说Flutter_Bloc是一个非常适合大型项目使用的状态管理框架。