封装flutter状态管理工具示例详解
作者:李小轰_Rex 发布时间:2022-04-17 14:15:56
标签:flutter,状态管理,工具封装
引言
关于 Flutter 状态管理,公司项目使用的是Bloc
方案。Bloc 其实本质上是 provider
的封装扩展库,整体通过 InheritedWidget
、Notifier
外加 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} 次',
);
}
}
来源:https://juejin.cn/post/7012537281726644254


猜你喜欢
- 1、Java主要特点简单性、跨平台性、分布性、安全性、健壮性、平 * 立与可移植性、多线程、动态性、面向对象的编程语言、支持垃圾自动收集处理等
- ArrayBlockingQueue介绍ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列。线程安全是指,ArrayB
- 如何通过Java发送HTTP请求,通俗点讲,如何通过Java(模拟浏览器)发送HTTP请求。Java有原生的API可用于发送HTTP请求,即
- 下面我们就字符串连接方面分析。1.String打开String的源码,如图所示会发现存储字符串的字符数值是final常量。再看String的
- 1.前言在java当中,若是进行比较,大家可能第一时间想到,==或是!=,这种数学上的比较符>、接下来,我就分别介绍并演示
- 本文实例为大家分享了C#实现网页画图的具体代码,供大家参考,具体内容如下代码贴着保存下using System;using System.C
- 本文将介绍Java在ICPC快速IO实现方法,下面看看
- multipartResolver上传文件配置1、gradle配置 compile ('commons-i
- 本文实例为大家分享了android仿支付宝密码输入框展示的具体代码,供大家参考,具体内容如下这个没什么好分析的,就是一些基本的绘制什么线,矩
- 公司的以前的项目,看到使用了这个Android自带的倒计时控件Chronometer,现在整合了一下先看看效果:<Chronomete
- 今天在开发的过程中,遇到java.lang.ExceptionInInitializerError异常,百度查了一下,顺便学习学习,做个笔记
- 本文实例讲述了C#时间戳基本用法。分享给大家供大家参考。具体如下:一、C#如何生成一个时间戳/// <summary> ///
- BeanFactoryBeanFactory是Spring中容器功能的基础,用于存放所有已经加载的bean,为了保证程序上的高可扩展性,Sp
- 在日常开发过程中时常需要用到设计模式,但是设计模式有23种,如何将这些设计模式了然于胸并且能在实际开发过程中应用得得心应手呢?和我一起跟着《
- 现在有很多库、实用工具和程序任Java开发人员选择。每个工具都有其优点,但其中有一些因它的知名度、多功能性和有效性从众多选项中脱颖而出。以下
- 1. 可空类型修饰符 ?// 引用类型能用空引用来表示一个不存在的值,但是值类型不能。例如:string str = null;int i
- 第一种就是 最常见的 用Try..Catch..再try中强转你要确认的string 类型成功就是int catch 就不是&n
- springboot项目出现”java: 错误: 无效的源发行版:17“问题解决方案下面是报错页面问
- 前面写过了使用ViewFlipper和ViewPager实现屏幕中视图切换的效果(ViewPager未实现轮播)附链接:ANDROID中使用
- 在装2个不同版本JDK时遇到了这个问题,在网上钩了一吧!查到一个讲解比较好的资料。一:要解决的问题我们在尝鲜 JDK1.5 的时候,相信不少