Flutter 对状态管理的认知与思考

2021-09-26  本文已影响0人  小呆呆666

前言

关于这篇文章的一些内容,我很久之前就想写的,但一直没啥源动力,就一直鸽着

这次被捷特大佬催了几次,终于把这篇文章写完了,文章里有我对状态管理的一些思考和看法,希望能引起茫茫人海中零星的共鸣。。。

image-20210925214350088 image-20210926090647120 image-20210925214254459

状态管理的认知

变迁

解耦是众多思想或框架的基石

就拿最最最经典的MVC来说,统一将模块分为三层

这个经典的层级划分能应付很多场景

时代总是在发展中前进,技术也在不停变迁;就像普罗米修斯盗火而来,给世间带来诸多变化

对View节点操作的思想,固定化的套用在如今的前端是不准确的

如今前端是由众多"状态"去控制界面展示的,需要用更加精炼的语言去阐述它

包容万千

状态管理的重点也就在其表面:状态和管理

状态是页面的灵魂,是业务逻辑和通用逻辑的锚定符,只要分离出状态,将其管理,就可以将页面解耦

一般来说,从状态管理的概念上,可以解耦出多个层级

极简模式 😃

这是一种十分简洁的层级划分,众多流行的Flutter状态管理框架,也是如此划分的,例如:provider,getx

极简模式

标准模式 🙂

这已经是一种类似MVC的层级划分了,这种层级也十分常见,例如:cubit(provider和getx也能轻松划分出这种结构)

标椎模式

严格模式 😐

对于标椎模式而言,已经划分的很到位了,但还有某一类层次没有划分出来:用户和程序交互的行为

说明下:想要划分出这一层级,代价必然是很大的,会让框架的使用复杂度进一步上升

常见的状态管理框架:Bloc,Redux,fish_redux

严格模式

强迫症模式 😑

常见的状态管理框架:Redux,fish_redux

从图上来看,这个结构已经有点复杂了,为了解耦数据刷新这一层次,付出了巨大的成本

强迫症模式

思考

对于变化的事物和思想,我们应该去恐惧,去抗拒吗?

我时常认为:优秀的思想见证变迁,它并不会在时光中衰败,而是变的越来越璀璨

例如:设计模式

解耦的成本

分离逻辑+状态层

一个成熟的状态管理框架,必定将逻辑从界面层里面划分处理,这是应该一个状态管理框架的最朴实的初衷

一些看法

实际上,此时付出的成本是针对框架开发者的,需要开发者去选择一个合适技术方案,去进行合理的解耦

实现一个状态管理框架,我此时,或许可以说:

此时,屏幕前的你可能会想了:这叼毛可真会吹牛皮,把👴逗笑了

img

关于上面的话,我真不是吹牛皮,我看了几个状态管理的源码后,发现状态管理的思想其实非常朴实,当然开源框架的代码并没有那么简单,基本都做了大量的抽象,方便功能扩展,这基本都会对阅读者产生极大的困扰,尤其是provider,看的头皮发麻、、、

我将几个典型的状态管理的思想提取出来后,用极简的代码复现其运行机制,发现用的都是观察模式的思想,理解了以后,就并不觉得状态管理框架多么的神秘了

我绝没有任何轻视的思想:他们都是那个莽荒时代里,伟大的拓荒者!

如何将逻辑+状态层从界面里解耦出来?

我总结了几种很经典的状态管理的实现机制,因为每一种实现源码都有点长,就放在文章后半截了,有兴趣的可以看看;每一种实现方式的代码都是完整的,可独立运行的

解耦逻辑层 划分状态层

Action层的成本

Action层是什么?正如其名字一样,行为层,用户和界面上的交互事件都可以划分到这一层

为什么要划分Action层?

基于业务会逐渐鬼畜的考量,一些框架划分出了Action层,统一管理了所有的交互事件

成本

框架侧成本

想要统一管理所有的交互事件,实现上难度不是很大

来看下实现思路

Action层划分思路

框架侧实现成本并不高,主要就是对事件的接受和分发

实际上,我们一般也不在乎框架侧成本,框架内部实现的再怎么复杂都无关紧要,用法应该简洁明了

如果内部设计非常精妙,使用起来却晦涩繁琐,无疑是给使用者增加心智负担

使用侧成本

划分出Action层,会给使用者增加一定的使用成本,这是无法避免的

图中红框的模块,是额外的使用成本

Action层使用成本

外在表现

Bloc不使用Action

class BlBlocCounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (BuildContext context) => BlBlocCounterBloc()..init(),
      child: Builder(builder: (context) => _buildPage(context)),
    );
  }

  Widget _buildPage(BuildContext context) {
    final bloc = BlocProvider.of<BlBlocCounterBloc>(context);

    return Scaffold(
      ...
      floatingActionButton: FloatingActionButton(
        //调用业务方法
        onPressed: () => bloc.increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}
class BlBlocCounterBloc extends Bloc<BlBlocCounterEvent, BlBlocCounterState> {
  BlBlocCounterBloc() : super(BlBlocCounterState().init());

  void init() async {
    ///处理逻辑,调用emit方法刷新
    emit(state.clone());
  }
    
  ...
}

state层:该演示中,此层不重要,不写了

Bloc使用Action

class BlBlocCounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (BuildContext context) => BlBlocCounterBloc()..add(InitEvent()),
      child: Builder(builder: (context) => _buildPage(context)),
    );
  }

  Widget _buildPage(BuildContext context) {
    final bloc = BlocProvider.of<BlBlocCounterBloc>(context);

    return Scaffold(
      ...
      floatingActionButton: FloatingActionButton(
        onPressed: () => bloc.add(AEvent()),
        child: Icon(Icons.add),
      ),
    );
  }
}
class BlBlocCounterBloc extends Bloc<BlBlocCounterEvent, BlBlocCounterState> {
  BlBlocCounterBloc() : super(BlBlocCounterState().init());

  @override
  Stream<BlBlocCounterState> mapEventToState(BlBlocCounterEvent event) async* {
    if (event is InitEvent) {
      yield await init();
    } else if (event is AEvent) {
      yield a();
    } else if (event is BEvent) {
      yield b();
    } else if (event is CEvent) {
      yield c();
    } else if (event is DEvent) {
      yield d();
    } else if (event is EEvent) {
      yield e();
    } else if (event is FEvent) {
      yield f();
    } else if (event is GEvent) {
      yield g();
    } else if (event is HEvent) {
      yield h();
    } else if (event is IEvent) {
      yield i();
    } else if (event is JEvent) {
      yield j();
    } else if (event is KEvent) {
      yield k();
    }
  }

  ///对应业务方法
  ...
}
abstract class BlBlocCounterEvent {}

class InitEvent extends BlBlocCounterEvent {}

class AEvent extends BlBlocCounterEvent {}

class BEvent extends BlBlocCounterEvent {}

class CEvent extends BlBlocCounterEvent {}

.......

class KEvent extends BlBlocCounterEvent {}

state层:该演示中,此层不重要,不写了

fish_redux的使用表现

Widget buildView(MainState state, Dispatch dispatch, ViewService viewService) {
  return Scaffold(
    //顶部AppBar
    appBar: mainAppBar(
      onTap: () => dispatch(MainActionCreator.toSearch()),
    ),
    //侧边抽屉模块
    drawer: MainDrawer(
      data: state,
      onTap: (String tag) => dispatch(MainActionCreator.clickDrawer(tag)),
    ),
    //页面主体
    body: MainBody(
      data: state,
      onChanged: (int index) => dispatch(MainActionCreator.selectTab(index)),
    ),
    //底部导航
    bottomNavigationBar: MainBottomNavigation(
      data: state,
      onTap: (int index) => dispatch(MainActionCreator.selectTab(index)),
    ),
  );
}
enum MainAction {
  //切换tab
  selectTab,
  //侧边栏item点击
  clickDrawer,
  //搜索
  toSearch,
  //统一刷新事件
  onRefresh,
}

class MainActionCreator {
  static Action toSearch() {
    return Action(MainAction.toSearch);
  }

  static Action selectTab(int index) {
    return Action(MainAction.selectTab, payload: index);
  }

  static Action onRefresh() {
    return Action(MainAction.onRefresh);
  }

  static Action clickDrawer(String tag) {
    return Action(MainAction.clickDrawer, payload: tag);
  }
}
Effect<MainState> buildEffect() {
  return combineEffects(<Object, Effect<MainState>>{
    //初始化
    Lifecycle.initState: _init,
    //切换tab
    MainAction.selectTab: _selectTab,
    //选择相应抽屉内部的item
    MainAction.clickDrawer: _clickDrawer,
    //跳转搜索页面
    MainAction.toSearch: _toSearch,
  });
}

///众多业务方法
void _init(Action action, Context<MainState> ctx) async {
  ...
}

fish_redux对Action层的划分以及事件的分发,明显要比Bloc老道很多

fish_redux使用枚举和一个类就完成了众多事件的定义;bloc需要继承类,一个类一个事件

老实说,俩种框架我都用了,bloc这样写确实比较麻烦,尤其涉及传参的时候,就需要在类里面定义很多变量

总结

上面几种形式对比,可以发现区别还是蛮大的

增加了Action层,使得使用成本不可避免的飙升

很多人心里,此时或许都会吐槽:好麻烦,,,

img

对Action层的思考和演化

通过对分离Action层的设计本质分析,我们会发现一个无法避免的现实!

Action层使用成本

当业务逐渐的复杂起来,Action层的划分是势在必行的,我们必须归纳事件入口;当业务频繁调整时,需要能快速的定位对应的业务!

有办法简化吗?

Action层的划分,会一定程度上增加使用者的负担,有什么办法可以简化呢?同时又能达到管理事件入口的效果?

我曾对View层疯狂套娃的Widget,做了很多思考,对拆分形式做了一些尝试

拆分后的效果,将View层和Action很好的结合起来了,具体操作:Flutter 改善套娃地狱问题(仿喜马拉雅PC页面举例)

carbon Action的演变

总结

框架的约定,可以规范众多行为习惯不同的开发者

后面我提出的对View层的拆分,只能依靠开发者本身的意识

这里,我给出一种不一样的方式,其中的取舍,只能由各位自己决定喽

我目前一直都是使用View层的拆分,自我感觉对后期复杂模块的维护,非常友好~~

Reducer层的吐槽

可能是我太菜了,一直感受不到这一层分化的妙处

我用fish_redux也写了很多页面(用了一年了),之前也会将相关数据通过Action层传递到Reducer,然后进行相应的刷新,这导致了一个问题!

被绕了多次,烦躁了多次后,我直接把Reducer层写成了一个刷新方法!

Reducer<WebViewState> buildReducer() {
  return asReducer(
    <Object, Reducer<WebViewState>>{
      WebViewAction.onRefresh: _onRefresh,
    },
  );
}

WebViewState _onRefresh(WebViewState state, Action action) {
  return state.clone();
}

就算在复杂的模块,我也没感受到他给我带来的好处,我就只能把他无限弱化成一个刷新方法了

img

状态管理的几种实现

这是我看了一些状态管理的源码

之前的几篇源码剖析文章写过,整理了下,做个总结

img

烂大街的实现

实现难度最小

这是一种非常常见的实现

实现

需要实现一个管理逻辑层实例的的中间件:依赖注入的实现

也可以使用InheritedWidget保存和传递逻辑层实例(Bloc就是这样做的);但是自己管理,可以大大拓宽使用场景,此处就自己实现一个管理实例的中间件

///依赖注入,外部可将实例,注入该类中,由该类管理
class Easy {
  ///注入实例
  static T put<T>(T dependency, {String? tag}) =>
      _EasyInstance().put(dependency, tag: tag);

  ///获取注入的实例
  static T find<T>({String? tag, String? key}) =>
      _EasyInstance().find<T>(tag: tag, key: key);

  ///删除实例
  static bool delete<T>({String? tag, String? key}) =>
      _EasyInstance().delete<T>(tag: tag, key: key);
}

///具体逻辑
class _EasyInstance {
  factory _EasyInstance() => _instance ??= _EasyInstance._();

  static _EasyInstance? _instance;

  _EasyInstance._();

  static final Map<String, _InstanceInfo> _single = {};

  ///注入实例
  T put<T>(T dependency, {String? tag}) {
    final key = _getKey(T, tag);
    //只保存第一次注入:针对自动刷新机制优化,每次热重载的时候,数据不会重置
    _single.putIfAbsent(key, () => _InstanceInfo<T>(dependency));
    return find<T>(tag: tag);
  }

  ///获取注入的实例
  T find<T>({String? tag, String? key}) {
    final newKey = key ?? _getKey(T, tag);
    var info = _single[newKey];

    if (info?.value != null) {
      return info!.value;
    } else {
      throw '"$T" not found. You need to call "Easy.put($T())""';
    }
  }

  ///删除实例
  bool delete<T>({String? tag, String? key}) {
    final newKey = key ?? _getKey(T, tag);
    if (!_single.containsKey(newKey)) {
      print('Instance "$newKey" already removed.');
      return false;
    }

    _single.remove(newKey);
    print('Instance "$newKey" deleted.');
    return true;
  }

  String _getKey(Type type, String? name) {
    return name == null ? type.toString() : type.toString() + name;
  }
}

class _InstanceInfo<T> {
  _InstanceInfo(this.value);
  T value;
}

定义一个监听和基类

class EasyXNotifier {
  List<VoidCallback> _listeners = [];

  void addListener(VoidCallback listener) => _listeners.add(listener);

  void removeListener(VoidCallback listener) {
    for (final entry in _listeners) {
      if (entry == listener) {
        _listeners.remove(entry);
        return;
      }
    }
  }

  void dispose() => _listeners.clear();

  void notify() {
    if (_listeners.isEmpty) return;

    for (final entry in _listeners) {
      entry.call();
    }
  }
}
class EasyXController {
  EasyXNotifier xNotifier = EasyXNotifier();

  ///刷新控件
  void update() => xNotifier.notify();
}

再来看看最核心的EasyBuilder控件:这就搞定了!

///刷新控件,自带回收机制
class EasyBuilder<T extends EasyXController> extends StatefulWidget {
  final Widget Function(T logic) builder;
  final String? tag;
  final bool autoRemove;

  const EasyBuilder({
    Key? key,
    required this.builder,
    this.autoRemove = true,
    this.tag,
  }) : super(key: key);

  @override
  _EasyBuilderState<T> createState() => _EasyBuilderState<T>();
}

class _EasyBuilderState<T extends EasyXController> extends State<EasyBuilder<T>> {
  late T controller;

  @override
  void initState() {
    super.initState();
    
    ///此处是整个类的灵魂代码
    controller = Easy.find<T>(tag: widget.tag);
    controller.xNotifier.addListener(() {
      if (mounted) setState(() {});
    });
  }

  @override
  void dispose() {
    if (widget.autoRemove) {
      Easy.delete<T>(tag: widget.tag);
    }
    controller.xNotifier.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) => widget.builder(controller);
}

使用

class EasyXCounterLogic extends EasyXController {
  var count = 0;

  void increase() {
    ++count;
    update();
  }
}
class EasyXCounterPage extends StatelessWidget {
  final logic = Easy.put(EasyXCounterLogic());

  @override
  Widget build(BuildContext context) {
    return BaseScaffold(
      appBar: AppBar(title: const Text('EasyX-自定义EasyBuilder刷新机制')),
      body: Center(
        child: EasyBuilder<EasyXCounterLogic>(builder: (logic) {
          return Text(
            '点击了 ${logic.count} 次',
            style: TextStyle(fontSize: 30.0),
          );
        }),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: Icon(Icons.add),
      ),
    );
  }
}
easy_x_builder

InheritedWidget的实现

实现具有一定的难度 ⭐⭐

更加详细的解析可查看:Flutter Provider的另一面

先来看下InheritedWidget它自带一些功能

数据传递

InheritedWidget存取数据

局部刷新

InheritedWidget对子节点的Element,有个强大的操作功能

InheritedWIdget刷新机制

有了上面这俩个关键知识,就可以轻松的实现一个强大的状态管理框架了,来看下实现

实现

class ChangeNotifierEasyP<T extends ChangeNotifier> extends StatelessWidget {
  ChangeNotifierEasyP({
    Key? key,
    required this.create,
    this.builder,
    this.child,
  }) : super(key: key);

  final T Function(BuildContext context) create;

  final Widget Function(BuildContext context)? builder;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    assert(
      builder != null || child != null,
      '$runtimeType  must specify a child',
    );

    return EasyPInherited(
      create: create,
      child: builder != null
          ? Builder(builder: (context) => builder!(context))
          : child!,
    );
  }
}

class EasyPInherited<T extends ChangeNotifier> extends InheritedWidget {
  EasyPInherited({
    Key? key,
    required Widget child,
    required this.create,
  }) : super(key: key, child: child);

  final T Function(BuildContext context) create;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => false;

  @override
  InheritedElement createElement() => EasyPInheritedElement(this);
}

class EasyPInheritedElement<T extends ChangeNotifier> extends InheritedElement {
  EasyPInheritedElement(EasyPInherited<T> widget) : super(widget);

  bool _firstBuild = true;
  bool _shouldNotify = false;
  late T _value;
  late void Function() _callBack;

  T get value => _value;

  @override
  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false;
      _value = (widget as EasyPInherited<T>).create(this);

      _value.addListener(_callBack = () {
        // 处理刷新逻辑,此处无法直接调用notifyClients
        // 会导致owner!._debugCurrentBuildTarget为null,触发断言条件,无法向后执行
        _shouldNotify = true;
        markNeedsBuild();
      });
    }

    super.performRebuild();
  }

  @override
  Widget build() {
    if (_shouldNotify) {
      _shouldNotify = false;
      notifyClients(widget);
    }
    return super.build();
  }

  @override
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    //此处就直接刷新添加的监听子Element了,不各种super了
    dependent.markNeedsBuild();
    // super.notifyDependent(oldWidget, dependent);
  }

  @override
  void unmount() {
    _value.removeListener(_callBack);
    _value.dispose();
    super.unmount();
  }
}
class EasyP {
  /// 获取EasyP实例
  /// 获取实例的时候,listener参数老是写错,这边直接用俩个方法区分了
  static T of<T extends ChangeNotifier>(BuildContext context) {
    return _getInheritedElement<T>(context).value;
  }

  /// 注册监听控件
  static T register<T extends ChangeNotifier>(BuildContext context) {
    var element = _getInheritedElement<T>(context);
    context.dependOnInheritedElement(element);
    return element.value;
  }

  /// 获取距离当前Element最近继承InheritedElement<T>的组件
  static EasyPInheritedElement<T>
      _getInheritedElement<T extends ChangeNotifier>(BuildContext context) {
    var inheritedElement = context
            .getElementForInheritedWidgetOfExactType<EasyPInherited<T>>()
        as EasyPInheritedElement<T>?;

    if (inheritedElement == null) {
      throw EasyPNotFoundException(T);
    }

    return inheritedElement;
  }
}

class EasyPNotFoundException implements Exception {
  EasyPNotFoundException(this.valueType);

  final Type valueType;

  @override
  String toString() => 'Error: Could not find the EasyP<$valueType>';
}
class EasyPBuilder<T extends ChangeNotifier> extends StatelessWidget {
  const EasyPBuilder(
    this.builder, {
    Key? key,
  }) : super(key: key);

  final Widget Function() builder;

  @override
  Widget build(BuildContext context) {
    EasyP.register<T>(context);
    return builder();
  }
}

大功告成,上面这三个类,就基于InheritedWidget自带的功能,实现了一套状态管理框架

使用

用法基本和Provider一摸一样...

class CounterEasyPPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierEasyP(
      create: (BuildContext context) => CounterEasyP(),
      builder: (context) => _buildPage(context),
    );
  }

  Widget _buildPage(BuildContext context) {
    final easyP = EasyP.of<CounterEasyP>(context);

    return Scaffold(
      appBar: AppBar(title: Text('自定义状态管理框架-EasyP范例')),
      body: Center(
        child: EasyPBuilder<CounterEasyP>(() {
          return Text(
            '点击了 ${easyP.count} 次',
            style: TextStyle(fontSize: 30.0),
          );
        }),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => easyP.increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}
class CounterEasyP extends ChangeNotifier {
  int count = 0;

  void increment() {
    count++;
    notifyListeners();
  }
}
easy_p

自动化刷新的实现

实现需要一些的灵感 ⭐⭐⭐

自动化刷新的实现

实现

同样的,需要管理其逻辑类的中间件;为了范例完整,再写下这个依赖管理类

///依赖注入,外部可将实例,注入该类中,由该类管理
class Easy {
  ///注入实例
  static T put<T>(T dependency, {String? tag}) =>
      _EasyInstance().put(dependency, tag: tag);

  ///获取注入的实例
  static T find<T>({String? tag, String? key}) =>
      _EasyInstance().find<T>(tag: tag, key: key);

  ///删除实例
  static bool delete<T>({String? tag, String? key}) =>
      _EasyInstance().delete<T>(tag: tag, key: key);
}

///具体逻辑
class _EasyInstance {
  factory _EasyInstance() => _instance ??= _EasyInstance._();

  static _EasyInstance? _instance;

  _EasyInstance._();

  static final Map<String, _InstanceInfo> _single = {};

  ///注入实例
  T put<T>(T dependency, {String? tag}) {
    final key = _getKey(T, tag);
    //只保存第一次注入:针对自动刷新机制优化,每次热重载的时候,数据不会重置
    _single.putIfAbsent(key, () => _InstanceInfo<T>(dependency));
    return find<T>(tag: tag);
  }

  ///获取注入的实例
  T find<T>({String? tag, String? key}) {
    final newKey = key ?? _getKey(T, tag);
    var info = _single[newKey];

    if (info?.value != null) {
      return info!.value;
    } else {
      throw '"$T" not found. You need to call "Easy.put($T())""';
    }
  }

  ///删除实例
  bool delete<T>({String? tag, String? key}) {
    final newKey = key ?? _getKey(T, tag);
    if (!_single.containsKey(newKey)) {
      print('Instance "$newKey" already removed.');
      return false;
    }

    _single.remove(newKey);
    print('Instance "$newKey" deleted.');
    return true;
  }

  String _getKey(Type type, String? name) {
    return name == null ? type.toString() : type.toString() + name;
  }
}

class _InstanceInfo<T> {
  _InstanceInfo(this.value);
  T value;
}
class EasyXNotifier {
  List<VoidCallback> _listeners = [];

  void addListener(VoidCallback listener) => _listeners.add(listener);

  void removeListener(VoidCallback listener) {
    for (final entry in _listeners) {
      if (entry == listener) {
        _listeners.remove(entry);
        return;
      }
    }
  }

  void dispose() => _listeners.clear();

  void notify() {
    if (_listeners.isEmpty) return;

    for (final entry in _listeners) {
      entry.call();
    }
  }
}

在自动刷新的机制中,需要将基础类型进行封装

///拓展函数
extension IntExtension on int {
  RxInt get ebs => RxInt(this);
}

extension StringExtension on String {
  RxString get ebs => RxString(this);
}

extension DoubleExtension on double {
  RxDouble get ebs => RxDouble(this);
}

extension BoolExtension on bool {
  RxBool get ebs => RxBool(this);
}

///封装各类型
class RxInt extends Rx<int> {
  RxInt(int initial) : super(initial);

  RxInt operator +(int other) {
    value = value + other;
    return this;
  }

  RxInt operator -(int other) {
    value = value - other;
    return this;
  }
}

class RxDouble extends Rx<double> {
  RxDouble(double initial) : super(initial);

  RxDouble operator +(double other) {
    value = value + other;
    return this;
  }

  RxDouble operator -(double other) {
    value = value - other;
    return this;
  }
}

class RxString extends Rx<String> {
  RxString(String initial) : super(initial);
}

class RxBool extends Rx<bool> {
  RxBool(bool initial) : super(initial);
}

///主体逻辑
class Rx<T> {
  EasyXNotifier subject = EasyXNotifier();

  Rx(T initial) {
    _value = initial;
  }

  late T _value;

  bool firstRebuild = true;

  String get string => value.toString();

  @override
  String toString() => value.toString();

  set value(T val) {
    if (_value == val && !firstRebuild) return;
    firstRebuild = false;
    _value = val;

    subject.notify();
  }

  T get value {
    if (RxEasy.proxy != null) {
      RxEasy.proxy!.addListener(subject);
    }
    return _value;
  }
}

需要写一个非常重要的中转类,这个也会储存响应式变量的监听对象

class RxEasy {
  EasyXNotifier easyXNotifier = EasyXNotifier();

  Map<EasyXNotifier, String> _listenerMap = {};

  bool get canUpdate => _listenerMap.isNotEmpty;

  static RxEasy? proxy;

  void addListener(EasyXNotifier notifier) {
    if (!_listenerMap.containsKey(notifier)) {
      //变量监听中刷新
      notifier.addListener(() {
        //刷新ebx中添加的监听
        easyXNotifier.notify();
      });
      //添加进入map中
      _listenerMap[notifier] = '';
    }
  }
}

刷新控件Ebx

typedef WidgetCallback = Widget Function();

class Ebx extends StatefulWidget {
  const Ebx(this.builder, {Key? key}) : super(key: key);

  final WidgetCallback builder;

  @override
  _EbxState createState() => _EbxState();
}

class _EbxState extends State<Ebx> {
  RxEasy _rxEasy = RxEasy();

  @override
  void initState() {
    super.initState();

    _rxEasy.easyXNotifier.addListener(() {
      if (mounted) setState(() {});
    });
  }

  Widget get notifyChild {
    final observer = RxEasy.proxy;
    RxEasy.proxy = _rxEasy;
    final result = widget.builder();
    if (!_rxEasy.canUpdate) {
      throw 'Widget lacks Rx type variables';
    }
    RxEasy.proxy = observer;
    return result;
  }

  @override
  Widget build(BuildContext context) {
    return notifyChild;
  }

  @override
  void dispose() {
    _rxEasy.easyXNotifier.dispose();

    super.dispose();
  }
}

在自动刷新机制中,回收依赖实例需要针对处理

此处我写了一个回收控件,可以完成实例的自动回收

class EasyBindWidget extends StatefulWidget {
  const EasyBindWidget({
    Key? key,
    this.bind,
    this.tag,
    this.binds,
    this.tags,
    required this.child,
  })  : assert(
          binds == null || tags == null || binds.length == tags.length,
          'The binds and tags arrays length should be equal\n'
          'and the elements in the two arrays correspond one-to-one',
        ),
        super(key: key);

  final Object? bind;
  final String? tag;

  final List<Object>? binds;
  final List<String>? tags;

  final Widget child;

  @override
  _EasyBindWidgetState createState() => _EasyBindWidgetState();
}

class _EasyBindWidgetState extends State<EasyBindWidget> {
  @override
  Widget build(BuildContext context) {
    return widget.child;
  }

  @override
  void dispose() {
    _closeController();
    _closeControllers();

    super.dispose();
  }

  void _closeController() {
    if (widget.bind == null) {
      return;
    }

    var key = widget.bind.runtimeType.toString() + (widget.tag ?? '');
    Easy.delete(key: key);
  }

  void _closeControllers() {
    if (widget.binds == null) {
      return;
    }

    for (var i = 0; i < widget.binds!.length; i++) {
      var type = widget.binds![i].runtimeType.toString();

      if (widget.tags == null) {
        Easy.delete(key: type);
      } else {
        var key = type + (widget.tags?[i] ?? '');
        Easy.delete(key: key);
      }
    }
  }
}

使用

class EasyXEbxCounterLogic {
  RxInt count = 0.ebs;

  ///自增
  void increase() => ++count;
}
class EasyXEbxCounterPage extends StatelessWidget {
  final logic = Easy.put(EasyXEbxCounterLogic());

  @override
  Widget build(BuildContext context) {
    return EasyBindWidget(
      bind: logic,
      child: BaseScaffold(
        appBar: AppBar(title: const Text('EasyX-自定义Ebx刷新机制')),
        body: Center(
          child: Ebx(() {
            return Text(
              '点击了 ${logic.count.value} 次',
              style: TextStyle(fontSize: 30.0),
            );
          }),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => logic.increase(),
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}
easy_x_ebx

最后

本文总体上,对状态管理的各个层次划分做了一些思考和一点个人的见解,文章后半截也给出了一些状态管理的实现方案

文章里的内容对想设计状态管理的靓仔,应该有一些帮助;如果你有相关不同的意见,欢迎在评论区讨论

img

相关地址

上一篇下一篇

猜你喜欢

热点阅读