flutter_bloc 是一个bloc第三方库,这个库很方便的让你集成bloc模式,这个库结合了RXDart。目前我们项目中就有用到rxdart。
bloc模式
BLoC是一种利用reactive programming方式构建应用的方法,这是一个由流构成的完全异步的世界。
常用的概念简介
-
reactive programming: 响应式编程 , 一种基于事件模式的模型。
-
Stream:流是一系列异步的数据.。
-
Observable实现并扩展了Stream。它将常用的stream和streamTransformer组合成了非常好用的api。你可以把它当成stream。
-
streamController:可以使用rxdart中的subjec替代streamController,subject可以视为streamController的增强版。根据数据流监听器个数的不同,Stream数据流可以分为单订阅流和多订阅流。
-
Subject:实现并扩展了StreamController,它符合StreamController的所有规范。假如之前使用的是StreamController,那么你可以直接替换为Subject。
-
StreamBuilder:包裹有状态的部件,streambuilder将会监听一个来自于BLoC的流,监听到新的数据,产生一个新的snapshot,并重新调用builder方法,Widget被重新构建。
工作流程图:
通过 StreamController 的 sink来添加数据,然后通过 StreamController 发送给 Stream,而订阅者则通过调用Stream的listen()方法来进行监听,listen()方法会返回一个 StreamSubscription 对象,StreamSubscription 对象支持对数据流进行暂停、恢复和取消等操作。
BLoC模式优点
相比传统的setState方式,StreamBuilder是一个很大的进步,因为它不需要强行重建整个组件树和它的子组件,只需要重建StreamBuilder包裹的组件即可.
- Cubit : 是
Stream
的一种特殊类型,用作Bloc
类的基础,一个Cubit
可以公开触发状态变化的函数。 - BlocProvider: Flutter小部件,可通过
BlocProvider.of<T>(context)
为其子级提供一个cubit。它用作依赖项注入widget,以便可以将子位的单个实例提供给子树中的多个小部件。
BlocProvider({
Key key,
//创建一个Cubit的实例
@required Create<T> create,
Widget child,
//默认情况下,BlocProvider将懒惰地创建cubit,这意味着当通过BlocProvider.of <BlocA>(上下文)查找cubit时,将执行创建。
bool lazy,
}) : this._(
key: key,
create: create,
dispose: (_, bloc) => bloc?.close(),
child: child,
lazy: lazy,
);
- BlocBuilder : BlocBuilder是一个Flutter小部件,它需要一个cubit和一个builder函数。 BlocBuilder处理构建小部件以响应新状态。 BlocBuilder与StreamBuilder非常相似,但具有更简单的API以减少所需的样板代码量。可能会多次调用builder函数,并且应该是一个纯粹的函数,该函数会根据状态返回小部件。与StreamBuilder的作用一样,但是它简化了StreamBuilder的实现细节,减少一部分必须的模版代码
const BlocBuilder({
Key key,
@required this.builder,
//如果省略cubit参数,则BlocBuilder将使用BlocProvider和当前的BuildContext自动执行查找。
C cubit,
//根据返回 true/false 来决定是否更新页面
BlocBuilderCondition<S> buildWhen,
}) : assert(builder != null),
super(key: key, cubit: cubit, buildWhen: buildWhen);
typedef BlocBuilderCondition<S> = bool Function(S previous, S current);
- MultiBlocProvider是Flutter小部件,将多个BlocProvider小部件合并为一个。 MultiBlocProvider提高了可读性,并且无需嵌套多个BlocProvider。
- BlocListener是一种Flutter小部件,它接受BlocWidgetListener和一个可选的cubit,并响应于cubit中的状态更改而调用侦听器。它应用于每次状态更改都需要发生一次的功能,例如导航,显示SnackBar,显示对话框等。仅当您希望提供无法通过BlocProvider和当前BuildContext访问的cubit时,才指定cubit。 (看源码得知,BlocBuilder内部就是封装了BlocListener)
BlocListener<BlocA, BlocAState>(
cubit: blocA,
listener: (context, state) {
//根据BlocA的中的值在此处进行操作,比如想弹个窗
}
)
可以在Android Studio看一个简单的示例。
获取cubit实例的方法有两种,一种是关注更新,一种是不关注更新:
- 不关注
// with extensions
context.read<BlocA>();
// without extensions
BlocProvider.of<BlocA>(context);
比如下图中的两个按钮:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t2Yh366o-1611042317793)(https://i.loli.net/2021/01/18/7jGlTcsA6zxVm4g.png)]
他俩是不需要进行重绘的,无论数值怎么变化,这两个按钮的UI是不需要更改的,那么此时获取cubit就可以使用上面的方法。
-
关注
// with extensions context.watch<BlocA>(); // without extensions BlocProvider.of<BlocA>(context, listen: true);
-
满足一定条件时更改
final isPositive = context.select((CounterBloc b) => b.state > 0);
如果CounterBloc的状态从正负之前切换,以上代码段才会重建。
Cubit
Cubit是 Stream
的一种特殊类型,用作 Bloc
类的基础 . 一个 Cubit
可以公开触发状态变化的函数。
状态是从
Cubit
中输出的,代表应用程序状态的一部分。可以通知UI组件状态,并根据当前状态重绘其自身的某些部分
创建 Cubit
时,我们需要定义 Cubit
将要管理的状态类型。对于上面的 CounterCubit
,状态可以通过 int
来表示,但在更复杂的情况下,可能有必要使用 class
(类)而不是原始类型。
class CounterCubit extends Cubit<int> {
CounterCubit(int initialState) : super(initialState);
void increment() => emit(state + 1);
}
每个 Cubit
都有能力通过 emit
输出一个新状态.
在上面的代码片段中,CounterCubit
公开了一个名为 increment
的公共方法,可以从外部调用该方法,以通知CounterCubit
增加其状态。当调用 increment
时,我们可以通过 state
获取器访问 Cubit
的当前状态,并通过向当前状态加 1
来发出 emit
新状态。
emit
函数受到保护,这意味着它只能在 Cubit
内部使用。
源码分析
//因为是 abstract , 所以我们可以重写它的方法来扩展实现想要的功能
abstract class Cubit<State> extends Stream<State> {
Cubit(this._state) {
_observer.onCreate(this);
}
State get state => _state;
BlocObserver get _observer => Bloc.observer;
StreamController<State> _controller;
State _state;
bool _emitted = false;
@protected
@visibleForTesting
void emit(State state) {
//初始化_controller 多订阅
_controller ??= StreamController<State>.broadcast();
//如果[Cubit]已关闭或发出的[state]等于当前[state],则[emit]不执行任何操作。
if (_controller.isClosed) return;
if (state == _state && _emitted) return;
onChange(Change<State>(currentState: this.state, nextState: state));
//将[状态]更新为提供的[状态]。
_state = state;
_controller.add(_state);
_emitted = true;
}
///通知[Cubit]触发[onError]的[错误]。
void addError(Object error, [StackTrace stackTrace]) {
onError(error, stackTrace);
}
///给定的[change]发生[change]时调用。
///当发出一个新的“状态”时发生[变化]。在更新“ cubit”的“ state”之前调用[onChange]。 [onChange]是为特定“cubit”添加日志记录分析的好地方
@mustCallSuper
void onChange(Change<State> change) {
// ignore: invalid_use_of_protected_member
_observer.onChange(this, change);
}
/// 每当[Cubit]中发生[错误]时调用。
/// 默认情况下,所有[错误]都将被忽略,并且[Cubit]功能将不受影响。
///一个在[Cubit]级别处理错误的好地方。
@protected
@mustCallSuper
void onError(Object error, StackTrace stackTrace) {
// ignore: invalid_use_of_protected_member
_observer.onError(this, error, stackTrace);
assert(() {
throw CubitUnhandledErrorException(this, error, stackTrace);
}());
}
/// 将订阅添加到Stream<State>中.
/// 返回一个[StreamSubscription],它使用提供的[onData],[onError]和[onDone]处理程序处理Stream <State>中的事件。
@override
StreamSubscription<State> listen(
void Function(State) onData, {
Function onError,
void Function() onDone,
bool cancelOnError,
}) {
_controller ??= StreamController<State>.broadcast();
return _controller.stream.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);
}
@override
bool get isBroadcast => true;
///关闭[Cubit]。
///当调用close时,将不再发出新状态。
@mustCallSuper
Future<void> close() {
_observer.onClose(this);
_controller ??= StreamController<State>.broadcast();
return _controller.close();
}
}
BlocListener
class _BlocListenerBaseState<C extends Cubit<S>, S>
extends SingleChildState<BlocListenerBase<C, S>> {
StreamSubscription<S> _subscription;
S _previousState;
C _cubit;
@override
void initState() {
super.initState();
_cubit = widget.cubit ?? context.read<C>();
_previousState = _cubit.state;
_subscribe();
}
@override
void didUpdateWidget(BlocListenerBase<C, S> oldWidget) {
super.didUpdateWidget(oldWidget);
final oldCubit = oldWidget.cubit ?? context.read<C>();
final currentCubit = widget.cubit ?? oldCubit;
if (oldCubit != currentCubit) {
if (_subscription != null) {
_unsubscribe();
_cubit = currentCubit;
_previousState = _cubit.state;
}
_subscribe();
}
}
@override
Widget buildWithChild(BuildContext context, Widget child) => child;
@override
void dispose() {
_unsubscribe();
super.dispose();
}
void _subscribe() {
if (_cubit != null) {
_subscription = _cubit.listen((state) {
if (widget.listenWhen?.call(_previousState, state) ?? true) {
widget.listener(context, state);
}
_previousState = state;
});
}
}
void _unsubscribe() {
if (_subscription != null) {
_subscription.cancel();
_subscription = null;
}
}
}