flutter_bloc is a bloc third-party library. This library is very convenient for you to integrate the bloc mode. This library combines RXDart. At present, rxdart is used in our project.
bloc mode
BLoC is a way to build applications using reactive programming, which is a completely asynchronous world composed of streams.
Introduction to commonly used concepts
-
reactive programming: Reactive programming, an event-based model.
-
Stream: A stream is a series of asynchronous data..
-
Observable implements and extends Stream. It combines the commonly used stream and streamTransformer into a very useful API. You can treat it as a stream.
-
streamController: Subjec in rxdart can be used instead of streamController, and subject can be regarded as an enhanced version of streamController. According to the number of data stream listeners, Stream data streams can be divided into single-subscription streams and multi-subscription streams.
-
Subject: Implements and extends StreamController, which conforms to all the specifications of StreamController. If you used StreamController before, you can directly replace it with Subject.
-
StreamBuilder: Wrapping stateful components, streambuilder will listen to a stream from BLoC, listen to new data, generate a new snapshot, and call the builder method again, and the Widget will be rebuilt.
work flow chart:
Add data through the sink of StreamController, and then send it to Stream through StreamController, while subscribers listen by calling Stream's listen() method. The listen() method will return a StreamSubscription object, which supports pausing the data stream, operations such as redo and cancel.
Advantages of BLoC mode
Compared with the traditional setState method, StreamBuilder is a great improvement, because it does not need to forcibly rebuild the entire component tree and its subcomponents, only need to rebuild the components wrapped by StreamBuilder.
- Cubit : is
Stream
a special type of that is used asBloc
the base of the class, aCubit
function that can expose trigger state changes. - BlocProvider: A Flutter widget that
BlocProvider.of<T>(context)
provides a cubit to its children. It is used as a dependency injection widget so that a single instance of a subbit can be provided to multiple widgets in a subtree.
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 is a Flutter widget that requires a cubit and a builder function. BlocBuilder handles building widgets in response to new states. BlocBuilder is very similar to StreamBuilder, but has a simpler API to reduce the amount of boilerplate code required. The builder function may be called multiple times and should be a pure function that returns the widget based on state. It has the same function as StreamBuilder, but it simplifies the implementation details of StreamBuilder and reduces some necessary template code
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 is a Flutter widget that combines multiple BlocProvider widgets into one. MultiBlocProvider improves readability and eliminates the need to nest multiple BlocProviders.
- A BlocListener is a Flutter widget that accepts a BlocWidgetListener and an optional cubit, and invokes the listener in response to state changes in the cubit. It should be used for functionality that needs to happen once per state change, such as navigation, showing a SnackBar, showing a dialog, etc. Specify a cubit only if you wish to provide a cubit that is not accessible through the BlocProvider and the current BuildContext. (Looking at the source code, we know that BlocBuilder encapsulates BlocListener inside)
BlocListener<BlocA, BlocAState>(
cubit: blocA,
listener: (context, state) {
//根据BlocA的中的值在此处进行操作,比如想弹个窗
}
)
A simple example can be seen in Android Studio.
There are two ways to get the cubit instance, one is to pay attention to the update, and the other is not to pay attention to the update:
- Not concerned
// with extensions
context.read<BlocA>();
// without extensions
BlocProvider.of<BlocA>(context);
For example, the two buttons in the figure below:
[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-t2Yh366o-1611042317793)(https://i.loli.net/2021/01/18/7jGlTcsA6zxVm4g.png )]
They do not need to be redrawn, no matter how the value changes, the UI of these two buttons does not need to be changed, then the above method can be used to obtain cubit at this time.
-
focus on
// with extensions context.watch<BlocA>(); // without extensions BlocProvider.of<BlocA>(context, listen: true);
-
Change when certain conditions are met
final isPositive = context.select((CounterBloc b) => b.state > 0);
The above code snippet will only be rebuilt if the state of CounterBloc is switched from positive to negative.
Cubit
Cubit is Stream
a special type of that is used as Bloc
the basis for the class. A Cubit
can expose functions that trigger state changes.
State is output
Cubit
from and represents part of the application state. Can notify UI component state and redraw some parts of itself according to the current state
When creating a Cubit
, we need to define Cubit
the type of state the will manage. For the above CounterCubit
, state can be int
represented by a , but in more complex cases it may be necessary to use class
(classes) instead of primitive types.
class CounterCubit extends Cubit<int> {
CounterCubit(int initialState) : super(initialState);
void increment() => emit(state + 1);
}
Each Cubit
has the ability emit
to output a new state via .
In the code snippet above, CounterCubit
a increment
public method named is exposed that can be called externally to notify the to CounterCubit
increment its state. When calling increment
, we can state
access the current state of the through the getter Cubit
, and 1
emit emit
a new state by adding to the current state.
emit
The function is protected, which means it can only Cubit
be used internally.
Source code analysis
//因为是 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;
}
}
}