AndroidFlutter

周末加班,我封装了一套 flutter 状态管理工具

2021-09-27  本文已影响0人  李小轰

引言

关于 Flutter 状态管理,公司项目使用的是Bloc方案。Bloc 其实本质上是 provider 的封装扩展库,整体通过 InheritedWidgetNotifier 外加 Stream中转实现状态变更通知。

关于 Bloc 实现原理,有兴趣的同学可以观看这篇文章 Bloc原理解析

RxBinder

撇开Bloc内部实现策略,小轰尝试基于数据驱动模型,自定义一套状态管理工具。构思如下:


主要成员如下:

代码实现

我们需要创建一个工具类用于注册和发送通知
///使用callback的形式管理通知
class RxNotifier {
  List<VoidCallback> _listeners = [];

  void addListener(VoidCallback listener) {
    _listeners.add(listener);
  }

  void remove(VoidCallback listener) {
    if (_listeners.contains(listener)) {
      _listeners.remove(listener);
    }
  }

  ///通知
  void notify() {
    if (_listeners.isEmpty) return;
    for (final entry in _listeners) {
      entry.call();
    }
  }
}
数据模型应该具备两个特性,当数据被使用时,添加监听。当数据发生改变时发送变更通知。
///数据模型基类,(封装变更通知)
class RxBinderData<T> {
  late T _value;
  late String uuid;
  RxNotifier subject = RxNotifier();
  RxBinder? _rxBinder;

  RxBinderData(this._value, {RxBinder? value}) {
    uuid = Uuid().v4();
    bindRx(value);
  }

  void bindRx(RxBinder? value) {
    _rxBinder = value;
  }

  @override
  String toString() {
    return value.toString();
  }

  T get value {
    //添加监听,变更通知注册
    _rxBinder?.register(uuid, subject);
    return _value;
  }

  set value(T val) {
    _value = val;
    notify();
  }

  void notify() {
    //通知数据发生变更
    subject.notify();
  }
}
创建一个中转工具类,用于统一管理数据变更时的消息分发和订阅关系
image.png
class RxBinder {
  Map<RxNotifier, String> _subjects = {};

  ///订阅者, key是订阅的数据id, value是订阅数据发生变化时的通知回调
  Map<String, List<VoidCallback>> _subscriber = {};

  //注册
  void register(String uuid, RxNotifier subject) {
    if (!_subjects.containsKey(subject)) {
      subject.addListener(() {
        _notify(uuid);
      });
      _subjects[subject] = '';
    }
  }

  //添加订阅关系
  void addListener(String uuid, VoidCallback listener) {
    if (!_subscriber.containsKey(uuid)) {
      //key不存在
      _subscriber[uuid] = [listener];
    } else {
      //key已存在
      List<VoidCallback> list = _subscriber[uuid]!;
      if (!list.contains(listener)) {
        list.add(listener);
        _subscriber[uuid] = list;
      }
    }
  }

  //通知订阅者
  void _notify(String uuid) {
    if (_subscriber.containsKey(uuid)) {
      final list = _subscriber[uuid];
      if (list != null && list.isNotEmpty) {
        for (final entry in list) {
          entry.call();
        }
      }
    }
  }
}
控制刷新组件
typedef WidgetCallback = Widget Function(BuildContext context);

class RxBindWidget extends StatefulWidget {
  final WidgetCallback builder;
  final List<RxBinderData> binders;

  const RxBindWidget(this.builder, this.binders, {Key? key}) : super(key: key);

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

class _RxBindWidgetState extends State<RxBindWidget> {
  RxBinder rxBinder = RxBinder();

  @override
  void initState() {
    super.initState();
    for (final entity in widget.binders) {
      //数据源绑定Rx
      entity.bindRx(rxBinder);
      rxBinder.addListener(entity.uuid, notifyDataChange);
    }
  }

  void notifyDataChange() {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(context);
  }
}

Demo 完美运行

///基础数据类型以int作为栗子
extension IntExtension on int {
  RxInt get rex => RxInt(this);

  //绑定Rx通知
  void bindRx(RxBinder? value) {
    rex.bindRx(value);
  }
}

///具体业务的扩展类
class RxInt extends RxBinderData<int> {
  RxInt(int value) : super(value);

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

  RxInt operator -(int other) {
    value = value - other;
    return this;
  }
}
class Logic {
  RxInt count = 0.rex;

  void increase() => ++count;
}

class TestRxBinder extends StatelessWidget {
  final Logic logic = Logic();

  TestRxBinder({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RxBindWidget((context) {
          return _child(context);
        }, [logic.count]),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: Icon(Icons.add),
      ),
    );
  }

  Widget _child(BuildContext context) {
    return Text(
      '点击了 ${logic.count.value} 次',
    );
  }
}

文中方案暂不完善,欢迎在评论区讨论 ~

上一篇下一篇

猜你喜欢

热点阅读