亲宝软件园·资讯

展开

封装flutter状态管理工具示例详解

李小轰_Rex 人气: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();
  }
}

创建一个中转工具类,用于统一管理数据变更时的消息分发和订阅关系

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} 次',
    );
  }
}

加载全部内容

相关教程
猜你喜欢
用户评论