看了Bloc源码后,心情有点复杂呀。。。
说点积极的...
用过Bloc的靓仔们,确定能感觉到,Bloc框架对开发页面,作了很清晰划分,框架强行定了俩种开发模式html
Bloc模式:该模式划分四层结构java
Cubit模式:该模式划分了三层结构git
做者在层次的划分上仍是很老道的,state层是直接写死在框架内部,这层必需要单独分出来;我感受若是不是被大型项目的克苏鲁代码山坑过,应该不会有这么深的执念github
这个state层加的,我以为至关有必要,由于某个页面一旦维护的状态不少,将状态变量和逻辑方法混在一块儿,后期维护会很是头痛。web
说点批判的...
你们可能在群里,常常看到一些老哥说:Bloc是将Provider封装了一层。api
我至关怀疑Bloc做者没看懂Provider的刷新机制浏览器
我上面吐槽了不少,并不是我对bloc有什么意见缓存
若是我理解有误,恳请你们指出,我真的很想找出点其中所蕴含的深意,改变我上面的想法。。。
app
为啥说心情复杂呢?
以前在看Provider源码的时候,看的有些头痛,内部逻辑确实有些复杂,可是总流程理通,刷新逻辑清晰以后,那是一种酣畅淋漓的感受!痛苦以后即是一种巨大的知足感,并对Provider熟练运用Framework层各类api,而后实现了精彩的刷新机制,感到赞叹!框架
而后,上面也讲了,我在Bloc上面确实花了一些精力,优化它的使用,而后看了他的源码,再想一想以前看的Provider源码,忽然有种巨大的落差感。
在我看来,这样大名鼎鼎的开源库,上面这点疙瘩彻底能够避免;也许是这种莫名的高期待,让我产生了这种落差。。。
这边介绍下使用,对官方的用法作了一些调整
调整心路的历程,可参照:flutter_bloc使用解析---骚年,你还在手搭bloc吗!
下面就直接写出调整后写法了
由于官方插件生成的写法,和调整后写法差距有点大,并且官方插件不支持生成view层和相关设置,此处我就撸了一个插件,完善了相关功能
请注意,Wrap代码和提示代码片断,参靠了官方插件规则
Wrap Widget 规则来着:intellij_generator_plugin
快捷代码生成规则来着: intellij_generator_plugin
插件可生成俩种模式代码:Bloc和Cubit;来看下
Cubit模式
class CounterPage extends StatelessWidget { final cubit = CounterCubit(); @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => cubit, child: Container(), ); } }
class CounterCubit extends Cubit<CounterState> { CounterCubit() : super(CounterState().init()); }
class CounterState { CounterState init() { return CounterState(); } CounterState clone() { return CounterState(); } }
Bloc模式
class CounterPage extends StatelessWidget { final bloc = CounterBloc(); @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => bloc..add(InitEvent()), child: Container(), ); } }
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(); } }
abstract class CounterEvent {} class InitEvent extends CounterEvent {}
class CounterState { CounterState init() { return CounterState(); } CounterState clone() { return CounterState(); } }
Bloc和Cubit模式对于结构,划分的很清楚,由于有多层结构划分,务必会有相应的模板代码和文件,没有插件的帮助,每次都写这些模板代码,会很是难受;这边为你们写了这个插件,若是有什么BUG,麻烦及时反馈哈。。。
这里就不重复写怎么使用了,使用明细可参照:flutter_bloc使用解析---骚年,你还在手搭bloc吗!
想弄懂Bloc原理,须要先了解下Stream的相关知识
StreamController、StreamBuilder:这俩者的搭配也能够轻松的实现刷新局部Widget,来看下使用
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(); } }
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(); } }
class StreamState { int count = 0; }
实际上,看了上述的使用,会发现有几个很麻烦的地方
Bloc做者借住Provider的InheritedProvider控件,将上面的痛点都解决了
Bloc的刷新机制很简单,上面的Stream操做,基本阐明了其核心的刷新机制,可是Bloc做者作了一些封装,咱们来看看
BlocProvider是一个很是重要的控件,刷新参数的精简和Stream流的关闭都和其有关,由于该封装了一个Provider里面InheritedProvider;可是,可是在我看来,他依旧是一个颇有魅力的控件
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自动关闭操做
create是外部实例化的XxxBloc,最终传入了InheritedProvider中
该实例直接传入了InheritedProvider中,这就是涉及到Provider中,最终是储存在 _InheritedProviderScopeElement中, _startListening也是Provider的内容
说真的 _startListening里面的逻辑没什么卵用
BlocProvider.of<T>
原理:源码篇:Flutter Provider的另外一面(万字图文+插件),仍是在这篇文章里
总结:来概括下BlocProvider这个类的做用
BlocProvider实现了上面这三个碉堡的功能,基本就能够把Stream使用模式完全精简了
毋庸置疑,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这个不重要,这是框架内部定义的一个类,这边能够忽略掉,不过重要
储存了传入的state对象
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对StreamBuilder的用法作了不少精简,来看下内部实现
BlocBuilder
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
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), ); } }
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, ); }
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刷新的核心逻辑
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(); } }
若是使用Bloc模式开发,会多出一个Event层,该层是定义全部的事件交互
这边提一下
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, ); } }
总体逻辑比较清晰,来理一下
Bloc是抽象类
Bloc类中,实例了Stream流对象,来作Event的事件触发机制
_bindEventsToStates里面作了一些操做
上面几个关键的类分析完,整个Bloc的运行机制,一会儿就明朗了
BlocProvider
BlocBase
BlocBuilder
数据改变后,经过setState重建StatefulWidget,以达到局部刷新的效果
# 手搓一个状态管理框架
Bloc的原理相对Provider而言,要简单不少。。。
模仿Bloc的刷新机制,来手搓一个状态管理框架!用EasyC来命名吧!
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
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(); } }
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原汁原味的用法
class CounterEasyCPage extends StatelessWidget { final easyC = CounterEasyC(); @override Widget build(BuildContext context) { return EasyCProvider( create: (BuildContext context) => easyC, child: 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), ), ), ); } }
class CounterEasyC extends EasyC<CounterEasyCState> { CounterEasyC() : super(CounterEasyCState().init()); ///自增 void increment() => emit(state.clone()..count = ++state.count); }
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这三个框架,我都写了相应插件,若是你选择的状态管理框架是这个三者中任意一个,相信这些插件,都能帮你完成一些重复的工做量
Web效果:https://cnad666.github.io/flu...
Windows:Windows平台安装包
系列文章