前言

看了Bloc源码后,心情有点复杂呀。。。

说点积极的...

用过Bloc的靓仔们,肯定能感受到,Bloc框架对开发页面,做了很清晰划分,框架强行定了俩种开发模式

  • Bloc模式:该模式划分四层结构

    • bloc:逻辑层
    • state:数据层
    • event:所有的交互事件
    • view:页面
  • Cubit模式:该模式划分了三层结构
    • cubit:逻辑层
    • state:数据层
    • view:页面

作者在层次的划分上还是很老道的,state层是直接写死在框架内部,这层必须要单独分出来;我感觉如果不是被大型项目的克苏鲁代码山坑过,应该不会有这么深的执念

这个state层加的,我觉得相当有必要,因为某个页面一旦维护的状态很多,将状态变量和逻辑方法混在一起,后期维护会非常头痛。

说点批判的...

  • 大家可能在群里,经常看到一些老哥说:Bloc是将Provider封装了一层。

    • 这里我证实下:这是真的,Bloc确实将Provider封了一层
    • 但是仅仅只用到Provider中子节点查询最近父节点InheritedElement数据和顶层Widget并列布局功能,Provider最经典的刷新机制,完全没用到!
  • 我觉得Bloc作者,对Provider的刷新机制可能有点迷糊

    • 哪怕bloc框架在build widget里用到了一行: Provider.of(context, listen: true) 或者去掉e.markNeedsNotifyDependents() ,我都不会说这话。。。
    • Bloc框架做了一些让我非常疑惑的操作,_startListening方法中的回调中调用了 e.markNeedsNotifyDependents()完全没用!因为没使用Provider.of(context, listen: true) 向 InheritedElement 添加子Element,所以是刷新了个寂寞!为了验证我的想法,我debug了 framework层的notifyClients方法,调用emit或yield刷新的时候, _dependents的map一直为空,哎。。。
  • 我上面吐槽了很多,并非我对bloc有什么意见

    • Bloc我也用了较长的时间,深度使用过程,对其用法做了一些优化,还为其写了一个代码生成插件,为它也算付出了一些时间和精力
    • 但是:代码是不会说谎的,所有好的或不好的都在其中,用心体悟就能感受到。

为啥说心情复杂呢?

之前在看Provider源码的时候,看的有些头痛,内部逻辑确实有些复杂,但是总流程理通,刷新逻辑清晰之后,那是一种酣畅淋漓的感觉!痛苦之后便是一种巨大的满足感,并对Provider熟练运用Framework层各种api,然后实现了精彩的刷新机制,感到赞叹!

然后,上面也讲了,我在Bloc上面确实花了一些精力,优化它的使用,然后看了他的源码,再想想之前看的Provider源码,突然有种巨大的落差感。

在我看来,这样大名鼎鼎的开源库,上面这点疙瘩完全可以避免;也许是这种莫名的高期待,让我产生了这种落差。。。

对了,说不定是Bloc作者,故意留了一个Provider刷新机制在Bloc中,把这个作为一个彩蛋!

突然感觉这点疙瘩没了!

使用

这边介绍下使用,对官方的用法做了一些调整

调整心路的历程,可参照:flutter_bloc使用解析---骚年,你还在手搭bloc吗!

下面就直接写出调整后写法了

插件

因为官方插件生成的写法,和调整后写法差距有点大,而且官方插件不支持生成view层和相关设置,此处我就撸了一个插件,完善了相关功能

请注意,Wrap代码和提示代码片段,参靠了官方插件规则

Wrap Widget 规则来着:intellij_generator_plugin

快捷代码生成规则来着: intellij_generator_plugin

  • 在Android Studio里面搜索 flutter bloc

  • 生成模板代码

  • 支持修改后缀

  • Wrap Widget (alt + enter):RepositoryProvider,BlocConsumer,BlocBuilder,BlocProvider,BlocListener

  • 输入 bloc 可生成快捷代码片段

用法

插件可生成俩种模式代码:Bloc和Cubit;来看下

Cubit模式

  • view
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => CounterCubit(),
child: Builder(builder: (context) => _buildPage(context)),
);
} Widget _buildPage(BuildContext context) {
final cubit = BlocProvider.of<CounterCubit>(context); return Container();
}
}
  • cubit
class CounterCubit extends Cubit<CounterState> {
CounterCubit() : super(CounterState().init());
}
  • state
class CounterState {
CounterState init() {
return CounterState();
} CounterState clone() {
return CounterState();
}
}

Bloc模式

  • view:默认添加了一个初始化事件
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => CounterBloc()..add(InitEvent()),
child: Builder(builder: (context) => _buildPage(context)),
);
} Widget _buildPage(BuildContext context) {
final bloc = BlocProvider.of<CounterBloc>(context); return Container();
}
}
  • bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState().init()); @override
Stream<CounterState> mapEventToState(CounterEvent event) async* {
if (event is InitEvent) {
yield await init();
}
} Future<CounterState> init() async {
return state.clone();
}
}
  • event
abstract class CounterEvent {}

class InitEvent extends CounterEvent {}
  • state
class CounterState {
CounterState init() {
return CounterState();
} CounterState clone() {
return CounterState();
}
}

总结

Bloc和Cubit模式对于结构,划分的很清楚,因为有多层结构划分,务必会有相应的模板代码和文件,没有插件的帮助,每次都写这些模板代码,会非常难受;这边为大家写了这个插件,如果有什么BUG,麻烦及时反馈哈。。。

这里就不重复写怎么使用了,使用明细可参照:flutter_bloc使用解析---骚年,你还在手搭bloc吗!

前置知识

想弄懂Bloc原理,需要先了解下Stream的相关知识

StreamController、StreamBuilder:这俩者的搭配也可以轻松的实现刷新局部Widget,来看下使用

  • view:Stream流必须要有关闭的操作,此处就需要使用StatefulWidget,需要它的dispose回调
class StreamPage extends StatefulWidget {
const StreamPage({Key? key}) : super(key: key); @override
_StreamPageState createState() => _StreamPageState();
} class _StreamPageState extends State<StreamPage> {
final logic = StreamLogic(); @override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Bloc-Bloc范例')),
body: Center(
child: StreamBuilder<StreamState>(
initialData: logic.state,
stream: logic.stream,
builder: (context, snapshot) {
return Text(
'点击了 ${snapshot.data!.count} 次',
style: TextStyle(fontSize: 30.0),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increment(),
child: Icon(Icons.add),
),
);
} @override
void dispose() {
logic.dispose();
super.dispose();
}
}
  • logic:Stream数据源是泛型,可以直接使用基础类型,此处使用实体,是为了后期可扩展更多数据
class StreamLogic {
final state = StreamState(); // 实例化流控制器
final _controller = StreamController<StreamState>.broadcast(); Stream<StreamState> get stream => _controller.stream; void increment() {
_controller.add(state..count = ++state.count);
} void dispose() {
// 关闭流控制器,释放资源
_controller.close();
}
}
  • state
class StreamState {
int count = 0;
}
  • 效果图

实际上,看了上述的使用,会发现有几个很麻烦的地方

  • 需要创建Stream的一系列对象
  • Stream流必须要有关闭操作,所以要使用StatefulWidget
  • StreamBuilder需要写三个参数,很麻烦

Bloc作者借住Provider的InheritedProvider控件,将上面的痛点都解决了

刷新机制

Bloc的刷新机制很简单,上面的Stream操作,基本阐明了其核心的刷新机制,但是Bloc作者做了一些封装,我们来看看

BlocProvider的魅力

BlocProvider是一个非常重要的控件,刷新参数的精简和Stream流的关闭都和其有关,因为该封装了一个Provider里面InheritedProvider;但是,但是在我看来,他依旧是一个很有魅力的控件

  • BlocProvider:BlocProvider的源码很简单,下面就是这个类的源码
class BlocProvider<T extends BlocBase<Object?>>
extends SingleChildStatelessWidget with BlocProviderSingleChildWidget {
/// {@macro bloc_provider}
BlocProvider({
Key? key,
required Create<T> create,
this.child,
this.lazy,
}) : _create = create,
_value = null,
super(key: key, child: child); BlocProvider.value({
Key? key,
required T value,
this.child,
}) : _value = value,
_create = null,
lazy = null,
super(key: key, child: child); /// Widget which will have access to the [Bloc] or [Cubit].
final Widget? child; final bool? lazy; final Create<T>? _create; final T? _value; static T of<T extends BlocBase<Object?>>(
BuildContext context, {
bool listen = false,
}) {
try {
return Provider.of<T>(context, listen: listen);
} on ProviderNotFoundException catch (e) {
if (e.valueType != T) rethrow;
throw FlutterError(
'''
BlocProvider.of() called with a context that does not contain a $T.
No ancestor could be found starting from the context that was passed to BlocProvider.of<$T>(). This can happen if the context you used comes from a widget above the BlocProvider. The context used was: $context
''',
);
}
} @override
Widget buildWithChild(BuildContext context, Widget? child) {
final value = _value;
return value != null
? InheritedProvider<T>.value(
value: value,
startListening: _startListening,
lazy: lazy,
child: child,
)
: InheritedProvider<T>(
create: _create,
dispose: (_, bloc) => bloc.close(),
startListening: _startListening,
child: child,
lazy: lazy,
);
} static VoidCallback _startListening(
InheritedContext<BlocBase> e,
BlocBase value,
) {
final subscription = value.stream.listen(
(dynamic _) => e.markNeedsNotifyDependents(),
);
return subscription.cancel;
}
}
  • BlocProvider和BlocProvider.value的区别

    • 看上面源码可知:BlocProvider.value没有做Stream自动关闭操作

      • 所以BlocProvider.value不应该在普通的单页面使用,可用于全局Bloc实例
    • 单页面Bloc请使用BlocProvider去创建Bloc或Cubit
  • create是外部实例化的XxxBloc,最终传入了InheritedProvider中

    • create就是外部传入的XxxBloc实例
    • 该实例直接传入了InheritedProvider中,这就是涉及到Provider中,最终是储存在 _InheritedProviderScopeElement中, _startListening也是Provider的内容
    • 说真的 _startListening里面的逻辑没什么卵用
      • markNeedsNotifyDependents这个api是Provider作者专门为Provider子Element刷新做的,必须配套 Provider.of(context, listen: true) 去注册Widget控件才行
      • 涉及逻辑太多,都在上面Provider源码剖析文章中,感兴趣的可以去看看
  • BlocProvider.of

    • 作用:可以在BlocProvider包裹的子控件中,获取到BlocProvider Create传入的XxxBloc
    • 请注意:如果使用BlocProvider父布局context是拿不到XxxBloc的,必须是BlocProvider的子布局
    • 原理:源码篇:Flutter Provider的另一面(万字图文+插件),还是在这篇文章里
      • 我真的不是推广这文章啊,BlocProvider这部分,Bloc用了太多Provider特性
      • Provider文章,我花了九牛二虎之力将原理剖析完,在此处,就没必要再做复读机了

总结:来归纳下BlocProvider这个类的作用

  1. BlocProvider或会储存外部传入的XxxBloc实例,XxxBloc类必须继承BlocBase
  2. BlocProvider存储的XxxBloc实例,可以通过BlocProvider.of获取到(必须是在BlocProvider或其子Widget)
  3. BlocProvider获取的实例XxxBloc能够自动释放;BlocProvider.value命名构造函数实例的XxxBloc不会自动释放

BlocProvider实现了上面这三个碉堡的功能,基本就可以把Stream使用模式彻底精简了

  • 图示

基石BlocBase

毋庸置疑,BlocBase是很重要的一个抽象类

  • BlocBase
abstract class BlocBase<State> {
BlocBase(this._state) {
Bloc.observer.onCreate(this);
} StreamController<State>? __stateController;
StreamController<State> get _stateController {
return __stateController ??= StreamController<State>.broadcast();
} State _state; bool _emitted = false; State get state => _state; Stream<State> get stream => _stateController.stream; @Deprecated(
'Use stream.listen instead. Will be removed in v8.0.0',
)
StreamSubscription<State> listen(
void Function(State)? onData, {
Function? onError,
void Function()? onDone,
bool? cancelOnError,
}) {
return stream.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);
} void emit(State state) {
if (_stateController.isClosed) return;
if (state == _state && _emitted) return;
onChange(Change<State>(currentState: this.state, nextState: state));
_state = state;
_stateController.add(_state);
_emitted = true;
} @mustCallSuper
void onChange(Change<State> change) {
Bloc.observer.onChange(this, change);
} @mustCallSuper
void addError(Object error, [StackTrace? stackTrace]) {
onError(error, stackTrace ?? StackTrace.current);
} @protected
@mustCallSuper
void onError(Object error, StackTrace stackTrace) {
Bloc.observer.onError(this, error, stackTrace);
assert(() {
throw BlocUnhandledErrorException(this, error, stackTrace);
}());
} @mustCallSuper
Future<void> close() async {
Bloc.observer.onClose(this);
await _stateController.close();
}
}

上面的BlocBase做了几件比较重要的事,来梳理下

Bloc.observer这个不重要,这是框架内部定义的一个类,这边可以忽略掉,不太重要

  1. 储存了传入的state对象

    • 每次使用emit刷新的时候,会将传入state替换之前存储state对象
    • emit做了一个判断,如果传入state和存储state对象相同,将不执行刷新操作(这就是我在State类里面,加clone方法的原因)
  2. 初始化了Stream一系列对象
  3. 封装了关闭Stream流的操作
  • 将上面的代码精简下
abstract class BlocBase<T> {
BlocBase(this.state) : _stateController = StreamController<T>.broadcast(); final StreamController<T> _stateController; T state; bool _emitted = false; Stream<T> get stream => _stateController.stream; void emit(T newState) {
if (_stateController.isClosed) return;
if (state == newState && _emitted) return;
state = newState;
_stateController.add(state);
_emitted = true;
} @mustCallSuper
Future<void> close() async {
await _stateController.close();
}
}

BlocBuilder

BlocBuilder对StreamBuilder的用法做了很多精简,来看下内部实现

  • BlocBuilder

    • 此处需要关注下builder参数; buildWhen是个判断是否需要更新的参数
    • build方法里面调用了builder,需要看下父类BlocBuilderBase
typedef BlocWidgetBuilder<S> = Widget Function(BuildContext context, S state);

class BlocBuilder<B extends BlocBase<S>, S> extends BlocBuilderBase<B, S> {
const BlocBuilder({
Key? key,
required this.builder,
B? bloc,
BlocBuilderCondition<S>? buildWhen,
}) : super(key: key, bloc: bloc, buildWhen: buildWhen); final BlocWidgetBuilder<S> builder; @override
Widget build(BuildContext context, S state) => builder(context, state);
}
  • BlocBuilderBase

    • context.read< B>() 和 Provider.of(this, listen: false)效果是一样的,就是对后者的一个封装
    • 此处通过context.read< B>() 拿到了 我们在 BlocProvider中传入的XxxBloc对象,赋值给了_BlocBuilderBaseState中的 _bloc变量
    • BlocBuilderBase抽象了一个build方法,在 _BlocBuilderBaseState中赋值给了 BlocListener
    • BlocBuilderBase还没法看出刷新逻辑,几个重要的参数:_bloc,listener,widget.build都传给了BlocListener;需要看下BlocListener的实现
abstract class BlocBuilderBase<B extends BlocBase<S>, S>
extends StatefulWidget {
const BlocBuilderBase({Key? key, this.bloc, this.buildWhen})
: super(key: key); final B? bloc; final BlocBuilderCondition<S>? buildWhen; Widget build(BuildContext context, S state); @override
State<BlocBuilderBase<B, S>> createState() => _BlocBuilderBaseState<B, S>();
} class _BlocBuilderBaseState<B extends BlocBase<S>, S>
extends State<BlocBuilderBase<B, S>> {
late B _bloc;
late S _state; @override
void initState() {
super.initState();
_bloc = widget.bloc ?? context.read<B>();
_state = _bloc.state;
} ... @override
Widget build(BuildContext context) {
...
return BlocListener<B, S>(
bloc: _bloc,
listenWhen: widget.buildWhen,
listener: (context, state) => setState(() => _state = state),
child: widget.build(context, _state),
);
}
}
  • BlocListener:参数传给父类的构造函数了,需要看下父类BlocListenerBase的实现
class BlocListener<B extends BlocBase<S>, S> extends BlocListenerBase<B, S>
const BlocListener({
Key? key,
required BlocWidgetListener<S> listener,
B? bloc,
BlocListenerCondition<S>? listenWhen,
Widget? child,
}) : super(
key: key,
child: child,
listener: listener,
bloc: bloc,
listenWhen: listenWhen,
);
}
  • BlocListenerBase:精简了一些逻辑代码
abstract class BlocListenerBase<B extends BlocBase<S>, S>
extends SingleChildStatefulWidget {
const BlocListenerBase({
Key? key,
required this.listener,
this.bloc,
this.child,
this.listenWhen,
}) : super(key: key, child: child); final Widget? child; final B? bloc; final BlocWidgetListener<S> listener; final BlocListenerCondition<S>? listenWhen; @override
SingleChildState<BlocListenerBase<B, S>> createState() =>
_BlocListenerBaseState<B, S>();
} class _BlocListenerBaseState<B extends BlocBase<S>, S>
extends SingleChildState<BlocListenerBase<B, S>> {
StreamSubscription<S>? _subscription;
late B _bloc;
late S _previousState; @override
void initState() {
super.initState();
_bloc = widget.bloc ?? context.read<B>();
_previousState = _bloc.state;
_subscribe();
} ... @override
Widget buildWithChild(BuildContext context, Widget? child) {
return child!;
} @override
void dispose() {
_unsubscribe();
super.dispose();
} void _subscribe() {
_subscription = _bloc.stream.listen((state) {
if (widget.listenWhen?.call(_previousState, state) ?? true) {
widget.listener(context, state);
}
_previousState = state;
});
} void _unsubscribe() {
_subscription?.cancel();
_subscription = null;
}
}

终于找了关键的代码了!

可以发现Bloc是通过 StreamController 和 listen配合实现刷新的

调用的 widget.listener(context, state),这个实现的方法是个setState,大家可以看看 _BlocBuilderBaseState这个类

_bloc.stream.listen(
(state) {
if (widget.listenWhen?.call(_previousState, state) ?? true) {
widget.listener(context, state);
}
_previousState = state;
},
);

精简BlocBuild

上面的BlocBuild的实现逻辑还是太绕,封装层级太多,下面写个精简版的BlocBuild

当然了,肯定会保留BlocBuild刷新的核心逻辑

class BlocEasyBuilder<T extends BlocBase<V>, V> extends StatefulWidget {
const BlocEasyBuilder({
Key? key,
required this.builder,
}) : super(key: key); final Function(BuildContext context, V state) builder; @override
_BlocEasyBuilderState createState() => _BlocEasyBuilderState<T, V>();
} class _BlocEasyBuilderState<T extends BlocBase<V>, V>
extends State<BlocEasyBuilder<T, V>> {
late T _bloc;
late V _state;
StreamSubscription<V>? _listen; @override
void initState() {
_bloc = BlocProvider.of<T>(context);
_state = _bloc.state; //数据改变刷新Widget
_listen = _bloc.stream.listen((event) {
setState(() {});
});
super.initState();
} @override
Widget build(BuildContext context) {
return widget.builder(context, _state);
} @override
void dispose() {
_listen?.cancel();
super.dispose();
}
}
  • 来看下效果图:详细的使用代码,请查看:flutter_use

Event机制

如果使用Bloc模式开发,会多出一个Event层,该层是定义所有的事件交互

这边提一下

  • Bloc:省略了一些代码
abstract class Bloc<Event, State> extends BlocBase<State> {
/// {@macro bloc}
Bloc(State initialState) : super(initialState) {
_bindEventsToStates();
} StreamSubscription<Transition<Event, State>>? _transitionSubscription; StreamController<Event>? __eventController;
StreamController<Event> get _eventController {
return __eventController ??= StreamController<Event>.broadcast();
} void add(Event event) {
if (_eventController.isClosed) return;
try {
onEvent(event);
_eventController.add(event);
} catch (error, stackTrace) {
onError(error, stackTrace);
}
} Stream<Transition<Event, State>> transformEvents(
Stream<Event> events,
TransitionFunction<Event, State> transitionFn,
) {
return events.asyncExpand(transitionFn);
} @protected
@visibleForTesting
@override
void emit(State state) => super.emit(state); Stream<State> mapEventToState(Event event); Stream<Transition<Event, State>> transformTransitions(
Stream<Transition<Event, State>> transitions,
) {
return transitions;
} @override
@mustCallSuper
Future<void> close() async {
await _eventController.close();
await _transitionSubscription?.cancel();
return super.close();
} void _bindEventsToStates() {
_transitionSubscription = transformTransitions(
transformEvents(
_eventController.stream,
(event) => mapEventToState(event).map(
(nextState) => Transition(
currentState: state,
event: event,
nextState: nextState,
),
),
),
).listen(
(transition) {
if (transition.nextState == state && _emitted) return;
try {
emit(transition.nextState);
} catch (error, stackTrace) {
onError(error, stackTrace);
}
},
onError: onError,
);
}
}

整体逻辑比较清晰,来理一下

  1. Bloc是抽象类

    • 构造函数里面调用 _bindEventsToStates() 方法
    • Bloc抽象了一个mapEventToState(Event event)方法,继承Bloc抽象类,必须实现该方法
  2. Bloc类中,实例了Stream流对象,来做Event的事件触发机制
    • 添加Event事件时,会触发 _bindEventsToStates() 方法中的listener回调
  3. _bindEventsToStates里面做了一些操作
    • 被添加的Event事件:events.asyncExpand(transitionFn);先将自身Event参数传入transitionFn方法中执行
    • transitionFn的逻辑是:将Event参数传入mapEventToState中,然后mapEventToState回传State对象
    • 然后触发listen回调,listen中,将state传emit中,然后触发刷新控件重建

总结

上面几个关键的类分析完,整个Bloc的运行机制,一下子就明朗了

BlocProvider

  • 负责储存 传入XxxBloc加以储存

  • 提供的of方法,可以在BlocProvider或其子节点位置,获取到储存的XxxBloc

  • 提供回收资源的回调(回收Stream流)

BlocBase

  • 储存了传入的state对象

  • 初始化了Stream一系列对象

  • 封装了关闭Stream流的操作

BlocBuilder

  • 本质是StatefulWidget
  • 通过BlocProvider获取到XxxBloc,再通过其listener方法监听数据改变
  • 数据改变后,通过setState重建StatefulWidget,以达到局部刷新的效果

手搓一个状态管理框架

Bloc的原理相对Provider而言,要简单很多。。。

模仿Bloc的刷新机制,来手搓一个状态管理框架!用EasyC来命名吧!

手搓

  • EasyC:首先需要写一个基类,处理Stream一系列的操作
abstract class EasyC<T> {
EasyC(this.state) : _controller = StreamController<T>.broadcast(); final StreamController<T> _controller; T state; bool _emitted = false; Stream<T> get stream => _controller.stream; void emit(T newState) {
if (_controller.isClosed) return;
if (state == newState && _emitted) return;
state = newState;
_controller.add(state);
_emitted = true;
} @mustCallSuper
Future<void> close() async {
await _controller.close();
}
}
  • EasyCProvider

    • 这里就不使用Provider框架提供的InheritedProvider了
    • 这边我用InheritedWidget手搓了一个
    • of方法和stream流的关闭都搞定了;不用手动关流,也不用写StatefulWidget了!
class EasyCProvider<T extends EasyC> extends InheritedWidget {
EasyCProvider({
Key? key,
Widget? child,
required this.create,
}) : super(key: key, child: child ?? Container()); final T Function(BuildContext context) create; @override
bool updateShouldNotify(InheritedWidget oldWidget) => false; @override
InheritedElement createElement() => EasyCInheritedElement(this); static T of<T extends EasyC>(BuildContext context) {
var inheritedElement =
context.getElementForInheritedWidgetOfExactType<EasyCProvider<T>>()
as EasyCInheritedElement<T>?; if (inheritedElement == null) {
throw 'not found';
} return inheritedElement.value;
}
} class EasyCInheritedElement<T extends EasyC> extends InheritedElement {
EasyCInheritedElement(EasyCProvider<T> widget) : super(widget); bool _firstBuild = true; late T _value; T get value => _value; @override
void performRebuild() {
if (_firstBuild) {
_firstBuild = false;
_value = (widget as EasyCProvider<T>).create(this);
} super.performRebuild();
} @override
void unmount() {
_value.close();
super.unmount();
}
}
  • EasyCBuilder:最后整一个定点刷新Widget
class EasyCBuilder<T extends EasyC<V>, V> extends StatefulWidget {
const EasyCBuilder({
Key? key,
required this.builder,
}) : super(key: key); final Function(BuildContext context, V state) builder; @override
_EasyCBuilderState createState() => _EasyCBuilderState<T, V>();
} class _EasyCBuilderState<T extends EasyC<V>, V>
extends State<EasyCBuilder<T, V>> {
late T _easyC;
late V _state;
StreamSubscription<V>? _listen; @override
void initState() {
_easyC = EasyCProvider.of<T>(context);
_state = _easyC.state; //数据改变刷新Widget
_listen = _easyC.stream.listen((event) {
setState(() {});
});
super.initState();
} @override
Widget build(BuildContext context) {
return widget.builder(context, _state);
} @override
void dispose() {
_listen?.cancel();
super.dispose();
}
}

上面这三个文件,基本就把Bloc的刷新机制再现了

同时,也去掉了我心中的一个疙瘩,Bloc源码对 Provider的 _startListening方法,莫名其妙的使用。。。

使用

使用基本和Bloc一摸一样

我本来想把emit俩个新旧state对象对比的判断去掉,但是想想Bloc作者对这个理念好像有很深的执念,在很多地方都做了处理;所以,这边我也就保留了,也可以保留Bloc原汁原味的用法

  • view
class CounterEasyCPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return EasyCProvider(
create: (BuildContext context) => CounterEasyC(),
child: Builder(builder: (context) => _buildPage(context)),
);
} Widget _buildPage(BuildContext context) {
final easyC = EasyCProvider.of<CounterEasyC>(context); return Scaffold(
appBar: AppBar(title: Text('自定义状态管理框架-EasyC范例')),
body: Center(
child: EasyCBuilder<CounterEasyC, CounterEasyCState>(
builder: (context, state) {
return Text(
'点击了 ${easyC.state.count} 次',
style: TextStyle(fontSize: 30.0),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => easyC.increment(),
child: Icon(Icons.add),
),
);
}
}
  • logic
class CounterEasyC extends EasyC<CounterEasyCState> {
CounterEasyC() : super(CounterEasyCState().init()); ///自增
void increment() => emit(state.clone()..count = ++state.count);
}
  • state
class CounterEasyCState {
late int count; CounterEasyCState init() {
return CounterEasyCState()..count = 0;
} CounterEasyCState clone() {
return CounterEasyCState()..count = count;
}
}
  • 效果图

全局也是可以的,和Provider没什么不一样,我这边就不重复写了

总结

这手搓的EasyC框架,保留Bloc刷新机制的精髓,同时,也做了大量的精简

相信有缘人只要用心看看,一定能够理解的

Bloc的源码并不复杂,他是对Stream的使用,做了一个大大的精简,基本使用痛点,全都封装起来,内部处理了

最后

留言板

Provider和Bloc的源码解析终于写完了,就差最后一篇GetX了。。。

为了证明我写的分析源码是有作用且有效果的,在末尾,我都根据其状态管理框架的刷新机制,手搓了一个全新的状态管理框架

选择状态管理框架,应该是一件比较慎重的事;事先可以先看看其原理,理解了他的内部运转机制,就完全可以去按需选择了,因为你明白了它的内部运转机制,就算使用过程中出现什么问题,你也能从容应对了;如果你怕作者弃坑或不满意其功能,选择你自己想要的刷新机制,自己去手搓一个!

Provider,Bloc,GetX这三个框架,我都写了相应插件,如果你选择的状态管理框架是这个三者中任意一个,相信这些插件,都能帮你完成一些重复的工作量

相关地址

系列文章

【源码篇】Flutter Bloc背后的思想,一篇纠结的文章的更多相关文章

  1. 《深入理解Spark:核心思想与源码分析》——SparkContext的初始化(叔篇)——TaskScheduler的启动

    <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...

  2. 鸿蒙内核源码分析(源码结构篇) | 内核每个文件的含义 | 百篇博客分析OpenHarmony源码 | v18.04

    百篇博客系列篇.本篇为: v18.xx 鸿蒙内核源码分析(源码结构篇) | 内核每个文件的含义 | 51.c.h .o 前因后果相关篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...

  3. [源码解析] PyTorch 分布式(8) -------- DistributedDataParallel之论文篇

    [源码解析] PyTorch 分布式(8) -------- DistributedDataParallel之论文篇 目录 [源码解析] PyTorch 分布式(8) -------- Distrib ...

  4. 一点一点看JDK源码(五)java.util.ArrayList 后篇之forEach

    一点一点看JDK源码(五)java.util.ArrayList 后篇之forEach liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 代 ...

  5. 一点一点看JDK源码(五)java.util.ArrayList 后篇之sort与Comparator

    一点一点看JDK源码(五)java.util.ArrayList 后篇之sort与Comparator liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JD ...

  6. 一点一点看JDK源码(五)java.util.ArrayList 后篇之SubList

    一点一点看JDK源码(五)java.util.ArrayList 后篇之SubList liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) S ...

  7. 一点一点看JDK源码(五)java.util.ArrayList 后篇之Spliterator多线程遍历

    一点一点看JDK源码(五)java.util.ArrayList 后篇之Spliterator多线程遍历 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看J ...

  8. 一点一点看JDK源码(五)java.util.ArrayList 后篇之removeIf与Predicate

    一点一点看JDK源码(五)java.util.ArrayList 后篇之removeIf与Predicate liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点 ...

  9. 一点一点看JDK源码(六)java.util.LinkedList前篇之链表概要

    一点一点看JDK源码(六)java.util.LinkedList前篇之链表概要 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.什么 ...

  10. 源码学习系列之SpringBoot自动配置(篇二)

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...

随机推荐

  1. mybatis查询参数Set遍历查询

    #sqlmapper <resultMap id="BaseResultMap" type="com.LogEntity" > <result ...

  2. 开发板测试手册——USB 4G 模块、GPS 定位功能操作步骤详解(3)

    目录 4 USB 4G 模块测试 41 4.1 网络功能测试 42 4.2 短信功能测试 43 4.3 GPS 定位功能测试 44 4.4 通话功能测试 45 4.5 测试程序编译 46 5 USB ...

  3. Babel 7 初探

    Babel有两大功能,转译和polyfill.转译就是把新的JS的语法,转化成旧的JS的语法.polyfill则是针对JS中新增的一些对象(Map, Set)和实例方法,这些对象和方法,在旧的浏览器中 ...

  4. SpringCloud学习篇

    什么是SpringCloud Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的集成.Spring cloud Task,一个生 ...

  5. Spring Boot集成Mybatis分页插件pagehelper

    引入依赖 <!--分页插件开始--> <dependency> <groupId>com.github.pagehelper</groupId> < ...

  6. docker-compose创建haproxy教程

    本文主要讲解通过docker-compose创建haproxy并进行代理 一.haproxy简介 HAProxy是一款基于事件驱动.单进程模型设计的四层与七层负载均衡器,它能够在TCP/UDP层面以及 ...

  7. 【RocketMQ 系列】 RocketMQ 双主双从(同步双写) 集群搭建

    1. 各角色介绍 Producer:消息的发送者:举例:发信者 Consumer:消息接收者:举例:收信者 Broker:暂存和传输信息:举例:邮局 NameServer:管理Broker:举例:各个 ...

  8. 从DDPM到DDIM

    从DDPM到DDIM (一) 现在网络上关于DDPM和DDIM的讲解有很多,但无论什么样的讲解,都不如自己推到一边来的痛快.笔者希望就这篇文章,从头到尾对扩散模型做一次完整的推导. DDPM是一个双向 ...

  9. [oeasy]python0027_整合程序_延迟输出时间_整合两个py程序

    ​ 整合程序 回忆上次内容 通过搜索发现 time中有函数可以延迟 time.sleep(1) 还可以让程序无限循环 while True: 现在需要两个程序的整合 循环延迟输出 时间输出 ​ 编辑 ...

  10. 阅读翻译Prompting Engineering Guides之Introduction(提示工程简介)

    阅读翻译Prompting Engineering Guides之Introduction(提示工程简介) 关于 首次发表日期:2024-07-19 Prompting Engineering Gui ...