Flutter 學(xué)習(xí)之旅(四十五) Flutter 狀態(tài) flutter_bloc學(xué)習(xí)(一)

在實(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 地址

https://github.com/tsm19911014/tsm_flutter

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容