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

目录
  • 引言
  • RxBinder
  • 代码实现
    • Demo 完美运行

引言

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

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

RxBinder

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

主要成员如下:

  • RxBinderData: 数据模型基类,内部封装变更通知
  • RxBinder: 核心类,用于关联订阅关系

代码实现

创建一个工具类用于注册和发送通知

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

以上就是封装flutter状态管理工具示例详解的详细内容,更多关于flutter状态管理封装的资料请关注我们其它相关文章!

(0)

相关推荐

  • flutter Bloc 实现原理示例解析

    目录 序言 1. 事件流 > 状态流 (中转) 2. 使用 BlocBuilder 实时监听状态变更, 如何实现的呢? 总结 扩展 序言 在flutter开发中,我们使用 bloc 框架,基于状态变更进行响应式开发.本篇文章,小轰将 bloc 核心业务块进行拆解简化,聊一聊它的实现思想,bloc 核心能力分为如下两点: 添加事件 event,将 '事件流' 转换为 '状态流' state 监听 bloc 流,每次 state 状态变更,通知 widget 更新 下面,用自定义Bloc的方式,来给

  • Flutter 阻止系统键盘弹出的优雅方式

    目录 前言 系统键盘弹出的原因 拦截系统键盘弹出信息 画自己的键盘 结语 前言 开篇先吐槽一下,输入框和文本,一直都是官方每个版本改动的重点,先不说功能上全不全的问题,每次版本升级,必有 breaking change .对于 extended_text_field | Flutter Package (flutter-io.cn) 和 extended_text | Flutter Package (flutter-io.cn) 来说,新功能都是基于官方的代码,每次版本升级,merge 代码就

  • Flutter在项目中使用动画不使用包实现详解

    目录 前言 正文 1 按下按钮柔软的感觉 2 想要一个像 Instagram 一样的喜欢按钮吗? 3 动画页面过渡 4 动画文字 5 更改/闪动文本样式 前言 动画对于 web 和移动应用程序都非常重要.但是在移动应用程序中不应该使用夸张的动画.简单但是很多动画使你的应用程序更好用.以至于当你点击一个按钮时,一种平滑的感觉或者页面过渡都会影响到你. 正文 1 按下按钮柔软的感觉 class _CustomButtonState extends State<CustomButton> with

  • Flutter runApp到渲染上屏分析详解

    目录 起源 分析准备 ensureInitialized scheduleAttachRootWidget scheduleWarmUpFrame 总结 起源 flutter作为一个跨平台的框架,在绘制上体现出了它跨平台的良好性能.那么,它是如何从runApp()后 绘制上屏的呢?本文将与你一起去探索这一过程. ps: 为了思维不中断, 本文仅对整体流程作分析,不会深入分析具体实现 我们运行一个flutter app ,入口一定是从runApp() 中进行的. 那么flutter 在runApp

  • flutter Bloc add两次只响应一次问题解析

    目录 问题描述 原因分析 处理方式 问题描述 连续调用两次addEvent,结果最终只能响应一次,第二次事件无法响应. @override Stream<SomeState> mapEventToState(SomeEvent event) async*{ if(event is InCreaseEvent){ state.num ++; yield state; } } someBloc.add(InCreaseEvent()); someBloc.add(InCreaseEvent());

  • Flutter 移动程序安全性提高的八个建议

    目录 正文 1. Obfuscate code 混淆代码 2. background snapshots 后台快照 3. Stay up-to-date 更新程序 4. Flushing in-memory cache 刷新内存缓存 5. local authentication 本地认证 6. Secure Storage 安全储存 7. Restrict network traffic 限制网络流量 8. jail-breaking protection 越狱保护 正文 这里有一些步骤,我们

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

    目录 引言 RxBinder 代码实现 Demo 完美运行 引言 关于 Flutter 状态管理,公司项目使用的是Bloc方案.Bloc 其实本质上是 provider 的封装扩展库,整体通过 InheritedWidget .Notifier 外加 Stream中转实现状态变更通知. 关于 Bloc 实现原理,有兴趣的同学可以观看这篇文章 Bloc原理解析 RxBinder 撇开Bloc内部实现策略,小轰尝试基于数据驱动模型,自定义一套状态管理工具.构思如下: 主要成员如下: RxBinder

  • Vue3新状态管理工具实例详解

    目录 前言 安装 创建Store State 定义State 获取state 修改state Getters Actions 异步action action间相互调用 数据持久化 安装 使用 自定义key 持久化部分state 最后 前言 Pinia.js 是新一代的状态管理器,由 Vue.js团队中成员所开发的,因此也被认为是下一代的 Vuex,即 Vuex5.x,在 Vue3.0 的项目中使用也是备受推崇. Pinia.js 有如下特点: 完整的 typescript 的支持: 足够轻量,压

  • flutter text组件使用示例详解

    目录 正文 Text组件 Text组件构造器上的主要属性 正文 flutter组件的实现参考了react的设计理念,界面上所有的内容都是由组件构成,同时也有状态组件和无状态组件之分,这里简单介绍最基本的组件. 在组件代码的书写方式上,web端开发的样式主要有由css进行控制,而客户端开发根据使用的技术栈不同,写法也稍微有些不同:ReactNative的写法和web比较类似,但是ReactNative是使用StyleSheet.create()方法创建样式对象,以内联的方式进行书写. import

  • linux 系统进程管理工具systemd详解(systemctl命令、创建自己的systemd服务)

    目录 linux systemd 什么是 systemd systemd 特点 unit(单元) systemd unit目录 Unit 和 Target Unit 文件结构 Linux命令——systemctl 参考 linux systemd 什么是 systemd Linux 系统在启动过程中,内核完成初始化以后,由内核第一个启动的程序便是 init 程序,路径为 /sbin/init(为一个软连接,链接到真实的 init 进程),其 PID 为1,它为系统里所有进程的“祖先”,Linux

  • Flutter CustomPaint自定义绘画示例详解

    目录 正文 CustomPaint 介绍 绘制点 PointMode3种模式 绘制线 和路径 绘制五子棋 总结 正文 CustomPaint是Flutter中用于自由绘制的一个widget,它与android原生的绘制规则基本一致,以当前Canves(画布)的左上角为原点进行绘制.在有些场景中,我们会需要绘制一些高度定制化的组件,比如 UI 设计师给我们出了个难题 —— 弄一个奇形怪状的边框.这个时候我们就不能直接使用 Flutter 自带的那些组件了,而是需要手动绘制组件,那就会需要用到 Cu

  • 为什么不推荐使用BeanUtils属性转换工具示例详解

    什么是BeanUtils工具 BeanUtils工具是一种方便我们对JavaBean进行操作的工具,是Apache组织下的产品. BeanUtils工具一般可以方便javaBean的哪些操作? 1)beanUtils 可以便于对javaBean的属性进行赋值. 2)beanUtils 可以便于对javaBean的对象进行赋值. 3)beanUtils可以将一个MAP集合的数据拷贝到一个javabean对象中. 1 背景 之前在专栏中讲过"不推荐使用属性拷贝工具",推荐直接定义转换类和方

  • Vue3状态管理的使用详解

    背景 随着Vue3的逐步应用,对状态管理的需求越来越多.起初是基于Vuex4进行状态管理的,但是Vuex4也暴露了一些问题.从个人角度来说,Vuex4类似于过渡期产品,对TypeScript的支持性并不完整.如果使用TypeScript编写组件,需要遵循一定步骤后,才可以正确进行类型推断,并且对modules的使用上也并不友好.Vuex核心贡献者Kia King也表示Vuex5已经在计划中,并且能提供完整的TypeScript支持,那么在Vuex5面世之前,或者直接"舍弃"Vuex的话

  • Spring AOP事务管理的示例详解

    目录 转账案例-环境搭建 步骤1:准备数据库表 步骤2:创建项目导入jar包 步骤3:根据表创建模型类 步骤4:创建Dao接口 步骤5:创建Service接口和实现类 步骤6:添加jdbc.properties文件 步骤7:创建JdbcConfig配置类 步骤8:创建MybatisConfig配置类 步骤9:创建SpringConfig配置类 步骤10:编写测试类 事务管理 转账案例-环境搭建 步骤1:准备数据库表 之前我们在整合Mybatis的时候已经创建了这个表,可以直接使用 create

  • Flutter 绘制风车实现示例详解

    目录 前言展示 1. 风车 1 的绘制 2. 风车 2 的绘制 3. 旋转动画的处理 4. 旋转动画的圈数 前言展示 最近源码看得比较多,本文来画点东西调节下心情,本绘制已收录于 FlutterUnit 的绘制集录,本文源码可参见[windmill.dart] .绘制内容非常简单,如下所示,两个样式的小风车:通过这两个小例子,可以学到: 路径的使用 画板的旋转变换 动画曲线与 Tween 的使用 风车1 风车2 1. 风车 1 的绘制 第一个风车非常简单,由四个 半圆 组成,每个部分直接的关系是

  • Vue中状态管理器(vuex)详解以及实际应用场景

    目录 Vue中 常见的组件通信方式可分为三类 Vuex简介 1. State 2. Getters 3. Mutations 4. Actions 5. 使用 mapState.mapGetters.mapActions 简化 总结 传送门:Vue中 子组件向父组件传值 及 .sync 修饰符 详解 传送门:Vue中 $ attrs.$ listeners 详解及使用 传送门:Vue中 事件总线(eventBus)详解及使用 传送门:Vue中 provide.inject 详解及使用 Vue中

随机推荐