关于android:源码篇Flutter-Bloc背后的思想一篇纠结的文章

38次阅读

共计 19271 个字符,预计需要花费 49 分钟才能阅读完成。

前言

看了 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<T>(context, listen: true) 或者 去掉 e.markNeedsNotifyDependents(),我都不会说这话。。。
    • Bloc 框架做了一些让我十分纳闷的操作,_startListening 办法中的回调中调用了 e.markNeedsNotifyDependents()齐全没用 !因为没应用Provider.of<T>(context, listen: true) 向 InheritedElement 增加子 Element,所以是刷新了个寂寞!为了验证我的想法,我 debug 了 framework 层的notifyClients 办法,调用 emit 或 yield 刷新的时候,_dependents 的 map 始终为空,哎。。。
  • 摈弃了 Provider 机制极简的 Callback 回调机制,抉择了 Stream 流这种。。。
  • 我下面吐槽了很多,并非我对 bloc 有什么意见

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

如果我了解有误,恳请大家指出,我真的很想找出点其中所蕴含的深意,扭转我下面的想法。。。

为啥说情绪简单呢?

之前在看 Provider 源码的时候,看的有些头痛,外部逻辑的确有些简单,然而总流程理通,刷新逻辑清晰之后,那是一种酣畅淋漓的感觉!苦楚之后便是一种微小的满足感,并对 Provider 纯熟使用 Framework 层各种 api,而后实现了精彩的刷新机制,感到赞叹!

而后,下面也讲了,我在 Bloc 下面的确花了一些精力,优化它的应用,而后看了他的源码,再想想之前看的 Provider 源码,忽然有种微小的落差感。

在我看来,这样赫赫有名的开源库,下面这点疙瘩齐全能够防止;兴许是这种莫名的高期待,让我产生了这种落差。。。

应用

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

调整心路的历程,可参照: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 {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 流必须要有敞开操作,所以要应用 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<T>(context, listen: true) 去注册 Widget 控件才行
      • 波及逻辑太多,都在下面 Provider 源码分析文章中,感兴趣的能够去看看
  • BlocProvider.of<T>

    • 作用:能够在 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<T> 获取到(必须是在 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<T>(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 {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),
        ),
      ),
    );
  }
}
  • 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 这三个框架,我都写了相应插件,如果你抉择的状态治理框架是这个三者中任意一个,置信这些插件,都能帮你实现一些反复的工作量

相干地址

  • 文章中 Demo 的 Github 地址:flutter_use
  • Web 成果:https://cnad666.github.io/flu…

    • 如果相干性能按钮没看到,可能须要你清下浏览器缓存
  • Windows:Windows 平台安装包

    • 明码:xdd666

系列文章

  • 源码篇:Flutter Provider 的另一面(万字图文 + 插件)
  • 源码篇:Handler 那些事(万字图文)
  • 源码篇:ThreadLocal 的奇思妙想(万字图文)

正文完
 0