在實(shí)際項(xiàng)目中我覺得大家一般不會(huì)直接使用StreamBuilder 的這種模式的BLoC,而是直接使用框架,網(wǎng)上經(jīng)常提到到的框架有scoped_model ,flutter_bloc,flutter_redux, privoder 這些框架,
scoped_model
這個(gè)在前面文章已經(jīng)分析過了,非常的小巧,利用了Microtask 微任務(wù)隊(duì)列做的異步通信和Flutter中InheritedWidget 控件的特性,可用于全局變量的傳遞,如果只是局部變量的傳遞,那么直接利用Element 的結(jié)構(gòu)樹的特性直接找到與祖先綁定widget,即可以獲取這個(gè)共享的數(shù)據(jù),
我們今天就來學(xué)習(xí)一下flutter_bloc 這個(gè)框架,學(xué)會(huì)用不是目的,提升自身才是最大的收益 ,我們使用的版本是從官網(wǎng)上找的最新代碼
flutter_bloc: ^6.0.6
我們先來看一下簡單的示例: 還是一個(gè)比較常見的計(jì)數(shù)器,這里我會(huì)根據(jù)官網(wǎng)的介紹,逐步的向高級(jí)用法延伸,所以前面的東西會(huì)比較簡單,但是不要認(rèn)為他不重要,即使是簡單也是前后配合完成了事件的通信,簡單的才是基礎(chǔ),非常重要
///監(jiān)聽類,BLoC礦建的靜態(tài)全局變量,這里打印了onChange 方法,
class TsmCountObserve extends BlocObserver{
@override
void onChange(Cubit cubit, Change change) {
printString('${cubit.runtimeType} '+" "+'$change');
super.onChange(cubit, change);
}
}
/// 實(shí)例類
class TsmCountCubit extends Cubit<int>{
TsmCountCubit(state) : super(state);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
class TsmFlutterBLoCPageBase extends StatelessWidget{
@override
Widget build(BuildContext context) {
Bloc.observer = TsmCountObserve();
return BlocProvider(
create: (context)=>TsmCountCubit(0),
child: TsmFlutterBLoCPage(),
);
}
}
class TsmFlutterBLoCPage extends StatefulWidget{
@override
State<StatefulWidget> createState() =>_TsmFlutterBLoCState();
}
class _TsmFlutterBLoCState extends State<TsmFlutterBLoCPage>{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter BLoC 學(xué)習(xí)'),
centerTitle: true,
),
body: BlocBuilder<TsmCountCubit,int>(
builder: (con,count){
return Container(
child: Text(count.toString()),
alignment: Alignment.center,
);
},
),
floatingActionButton: FloatingActionButton(
key: const Key('counterView_increment_floatingActionButton'),
child: const Icon(Icons.add),
onPressed: () => context.bloc<TsmCountCubit>().increment(),
),
);
}
}
使用flutter_bloc 框架的基礎(chǔ)流程
由于Observe 是全局靜態(tài)變量為了打印日志,可有可無,所以他不在使用flutter_bloc這個(gè)框架的基礎(chǔ)流程中,
1>創(chuàng)建Privoider
如果大家從網(wǎng)上看到的代碼的話,這個(gè)provider 一般包裹的是MaterialApp()這個(gè)類,這樣更能體現(xiàn)出他的數(shù)據(jù)傳遞的特性,由于我這邊demo涉及到關(guān)于bloc的代碼太多,不能讓每一個(gè)provider 都包裹MaterialApp,這里我讓他包裹了一個(gè)StatefulWidget 來演示數(shù)據(jù)的傳遞,
Privoider的入?yún)⒈容^簡單,一個(gè)事件源,一個(gè)child,實(shí)際還有一個(gè)lazy 這個(gè)屬性,用來控制懶加載的,
2>BlocBuilder<Event,State>();接收數(shù)據(jù)
BlocBuilder來包裹在數(shù)據(jù)改變時(shí)需要變更的控件,參數(shù)包含一個(gè)build 用來構(gòu)建控件的,注意他這里也可以提供另外的一個(gè)Cubit<T> , 這里可以解釋為事件源可能會(huì)有多個(gè),你可以指定接收哪個(gè)信號(hào)源的信息,那么默認(rèn)情況下是接收根 Privoider 提供的數(shù)據(jù)源,還是 直接包裹他的數(shù)據(jù)源呢,這里我們留一下一個(gè)疑問,在下面介紹源碼的時(shí)候我們?cè)俑鶕?jù)源碼具體說明,
bloc 發(fā)送數(shù)據(jù)
這里面非常巧妙的使用了拓展方法,將這個(gè)bloc<T>() 方法添加到了buildContext 的方法里面,具體實(shí)現(xiàn)代碼非常簡單就一行
extension BlocProviderExtension on BuildContext {
C bloc<C extends Cubit<Object>>() => BlocProvider.of<C>(this);
}
最簡單的實(shí)現(xiàn)方式已經(jīng)完成了,雖然代碼寫起來非常簡單,但是這里面有幾個(gè)問題需要我們思考一下,
1.provider 既然可以包裹在MaterialApp()外層,肯定是使用了InheritedWidget,那么他是如何來存儲(chǔ)這么需要的這個(gè)Cubit<T>數(shù)據(jù)呢?
2..既然是bloc模式,那么StreamController 的close 是何時(shí)調(diào)用的,怎么調(diào)用的呢,
3.同時(shí)存在根Cubit<T>和父Cubit<T> 的情況下,在BLoCBuilder 默認(rèn)情況下會(huì)主動(dòng)獲取哪個(gè)呢,
我們先看第三個(gè)問題,由于flutter_bloc的代碼中引用了太多的provider的方法,所以這里面很多的方法和類都是使用Provider 這個(gè)包下面的,
我們先來看看provider包下面的這個(gè)方法,具體返回的是什么,
static T of<T>(BuildContext context, {bool listen = true}) {
/// 獲取element
final inheritedElement = _inheritedElementOf<T>(context);
if (listen) {
///如果listen為true , 則簡歷這個(gè)context與inheritedWidget 之間的聯(lián)系
context.dependOnInheritedElement(inheritedElement);
}
return inheritedElement.value;
}
static _InheritedProviderScopeElement<T> _inheritedElementOf<T>(BuildContext context, ) {
_InheritedProviderScopeElement<T> inheritedElement;
///如果這個(gè)控件本身就是 InheritedWidget
if (context.widget is _InheritedProviderScope<T>) {
///遍歷這個(gè)控件的祖先Element,找到第一個(gè)就打斷,
context.visitAncestorElements((parent) {
inheritedElement = parent.getElementForInheritedWidgetOfExactType<
_InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>;
return false;
});
} else {
///通過這個(gè)_inheritedWidgets Map來獲取的數(shù)據(jù),所以這里不是遍歷,而是直接根據(jù)類型來獲取,也就是說,如果存在相同類型的
///inheritedWidgets 則后面的會(huì)替換掉前面已經(jīng)添加進(jìn)去的類型,即找到最近一個(gè)
inheritedElement = context.getElementForInheritedWidgetOfExactType<
_InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>;
}
if (inheritedElement == null) {
throw ProviderNotFoundException(T, context.widget.runtimeType);
}
return inheritedElement;
}
在_inheritedElementOf()這個(gè)方法里面告訴我們的答案就是找到最近的那個(gè),
我們?cè)賮砜纯吹?個(gè)問題 StreamController 的 close 是何時(shí)調(diào)用的,怎么調(diào)用的呢,
說道這里我們就不得不重新提一個(gè)Element 的生命周期的問題,在面前Element章節(jié)有說過傳送門 ( http://www.itdecent.cn/p/592561041c86 )
StatefulElement的管理著StatefulState 的生命方法,說道這里如果對(duì)element 的方法有過了解的肯定可以想到,這個(gè)dispse(),會(huì)由element的哪個(gè)方法去調(diào)用,沒錯(cuò)就是unmount() 方法
///StatefulElement 的 unmount() 方法
@override
void unmount() {
super.unmount();
_state.dispose();
_state._element = null;
_state = null;
}
我在看源碼的過程中并沒有找到provider他這個(gè)包里面包裹InheritedWidget 方法,所以就試著看一下Element 的源碼,在他們unmount 方法中找到了dispose()這個(gè)方法的調(diào)用時(shí)機(jī),
下面我們跟著源碼來走一遍他的流程,我只是看了一個(gè)大概,部分實(shí)現(xiàn)邏輯比較多,我也沒有具體看,
BlocProvider({
Key key,
@required CreateBloc<T> create,
Widget child,
bool lazy,
}) : this._(
key: key,
create: create,
dispose: (_, bloc) => bloc?.close(),
child: child,
lazy: lazy,
);
@override
Widget buildWithChild(BuildContext context, Widget child) {
return InheritedProvider<T>(
create: _create,
dispose: _dispose,
child: child,
lazy: lazy,
);
}
首先這個(gè)BlocProvider 在 初始化的時(shí)候會(huì)調(diào)用_()的這個(gè)方法,什么也沒有干,目的是為了封裝這個(gè)dispose()的方法,并在buildWithChild 方法中又調(diào)用了Provider 包中 InheritedProvider,將它初始化的變量傳遞了過去,
InheritedProvider({
Key key,
Create<T> create,
T update(BuildContext context, T value),
UpdateShouldNotify<T> updateShouldNotify,
void Function(T value) debugCheckInvalidValueType,
StartListening<T> startListening,
Dispose<T> dispose,
this.builder,
bool lazy,
Widget child,
}) : _lazy = lazy,
_delegate = _CreateInheritedProvider(
create: create,
update: update,
updateShouldNotify: updateShouldNotify,
debugCheckInvalidValueType: debugCheckInvalidValueType,
startListening: startListening,
dispose: dispose,
),
super(key: key, child: child);
@override
Widget buildWithChild(BuildContext context, Widget child) {
assert(
builder != null || child != null,
'$runtimeType used outside of MultiProvider must specify a child',
);
return _InheritedProviderScope<T>(
owner: this,
child: builder != null
? Builder(
builder: (context) => builder(context, child),
)
: child,
);
}
///這個(gè) Delegate 類似StatefulWidget
class _CreateInheritedProvider<T> extends _Delegate<T> {
_CreateInheritedProvider({
this.create,
this.update,
UpdateShouldNotify<T> updateShouldNotify,
this.debugCheckInvalidValueType,
this.startListening,
this.dispose,
}) : assert(create != null || update != null),
_updateShouldNotify = updateShouldNotify;
final Create<T> create;
final T Function(BuildContext context, T value) update;
final UpdateShouldNotify<T> _updateShouldNotify;
final void Function(T value) debugCheckInvalidValueType;
final StartListening<T> startListening;
final Dispose<T> dispose;
@override
_CreateInheritedProviderState<T> createState() =>
_CreateInheritedProviderState();
}
在這個(gè)InheritedProvider 方法中其實(shí)干的事情也不過,初始化了一個(gè)類似StatefulWidget的 delegate ,并在buildWithChild方法中創(chuàng)建了真正的保存數(shù)據(jù)的主角 _InheritedProviderScope
class _InheritedProviderScope<T> extends InheritedWidget {
_InheritedProviderScope({
this.owner,
@required Widget child,
}) : super(child: child);
final InheritedProvider<T> owner;
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return false;
}
@override
_InheritedProviderScopeElement<T> createElement() {
return _InheritedProviderScopeElement<T>(this);
}
}
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
@override
void unmount() {
_delegateState.dispose();
super.unmount();
}
}
最后發(fā)現(xiàn)在_InheritedProviderScopeElement 的unmount() 調(diào)用了最初由BLoCProvider 提供的這個(gè)dispose方法,
State 中的dispose 的實(shí)質(zhì)就是element 的unmount 方法的調(diào)用,只要這個(gè)unmount 方法執(zhí)行了,有沒有這個(gè)state并不重要,這也就是為什么要說這個(gè)dispose 方法的原因
看完了上面的代碼,再來看第一個(gè)問題,就非常簡單了,我們?cè)賮砜匆幌聀rovider.of<T>()的這個(gè)方法,
static T of<T>(BuildContext context, {bool listen = true}) {
/// 獲取element
final inheritedElement = _inheritedElementOf<T>(context);
if (listen) {
///如果listen為true , 則簡歷這個(gè)context與inheritedWidget 之間的聯(lián)系
context.dependOnInheritedElement(inheritedElement);
}
return inheritedElement.value;
}
最后獲取的是inheritedElement.value , 也就是_InheritedProviderScopeElement 的value ,這么這個(gè)value怎么來的呢,
看看下面_InheritedProviderScopeElement 的 performRebuild()這個(gè)方法
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
///共享的數(shù)據(jù)
@override
T get value => _delegateState.value;
_DelegateState<T, _Delegate<T>> _delegateState;
@override
void performRebuild() {
if (_firstBuild) {
_firstBuild = false;
_delegateState = widget.owner._delegate.createState()..element = this;
}
super.performRebuild();
}
}
從上面看這個(gè)delegatestate.value 是由_InheritedProviderScope.createState()后提供的,我們?cè)賮砜匆幌逻@個(gè)createState()方法
@override
T get value {
bool _debugPreviousIsInInheritedProviderCreate;
bool _debugPreviousIsInInheritedProviderUpdate;
if (!_didInitValue) {
_didInitValue = true;
if (delegate.create != null) {
try {
_value = delegate.create(element);
} finally {
}
}
if (delegate.update != null) {
try {
_value = delegate.update(element, _value);
} finally {
}
}
}
element._isNotifyDependentsEnabled = false;
_removeListener ??= delegate.startListening?.call(element, _value);
element._isNotifyDependentsEnabled = true;
assert(delegate.startListening == null || _removeListener != null);
return _value;
}
這個(gè)value的類型就是我們最開始由BLoCProvicer 傳遞的這個(gè)create的方法創(chuàng)建的,但是數(shù)據(jù)是否是我們最開始的數(shù)據(jù),就看是否對(duì)他做了修改,
至此,flutter_bloc的基礎(chǔ)功能就完事了,但是flutter_bloc 的精髓并不是這些,大家可以看到,基礎(chǔ)功能大多數(shù)都是使用了provider的這個(gè)包來完成的,他只是做了少量的封裝,如果業(yè)務(wù)不是很復(fù)雜,到這里已經(jīng)夠用了,剩下的我會(huì)在后續(xù)文章中繼續(xù)說
我學(xué)習(xí)flutter的整個(gè)過程都記錄在里面了
http://www.itdecent.cn/c/36554cb4c804
最后附上demo 地址