源码篇:Flutter Bloc背后的思想,一篇纠结的文章 (2)

view

class CounterPage extends StatelessWidget { final cubit = CounterCubit(); @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => cubit, child: 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 { final bloc = CounterBloc(); @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => bloc..add(InitEvent()), child: 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的一系列对象

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的内容

这内部的原理是比较复杂且很重要的,感兴趣请查看:源码篇:Flutter 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这个类的作用

BlocProvider或会储存外部传入的XxxBloc实例,XxxBloc类必须继承BlocBase

BlocProvider存储的XxxBloc实例,可以通过BlocProvider.of获取到(必须是在BlocProvider或其子Widget)

BlocProvider获取的实例XxxBloc能够自动释放;BlocProvider.value命名构造函数实例的XxxBloc不会自动释放

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

图示

BlocProvider

基石BlocBase

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwsxyx.html