# flutter_ref **Repository Path**: AndroidLst/flutter_ref ## Basic Information - **Project Name**: flutter_ref - **Description**: 为flutter开发的响应式组合api - **Primary Language**: Dart - **License**: BSD-3-Clause - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2023-11-07 - **Last Updated**: 2023-11-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [China](./README.md) Ref: 这是一个支持flutter数据响应式的组合Api插件

响应式,简单,灵活,可组合,可移植性高,侵入性低。支持数据同步修改,缓存队列异步刷新。 ## 特征 - Ref:把数据包装成一个响应式对象 - RefBuilder:数据修改通知对应的widget刷新 - Ref.update:内部监听所有响应对象的读取和修改,执行副作用并更新widget - RefCompute:支持计算属性 - RefWatch:数据修改,执行副作用 - RefKey:可以很方便的代理复杂响应式对象的某一个属性值 - refKeys: 函数,可以把一个复杂响应式对象的所有键代理到一个简单对象中,返回一个Map对象 - ... ## 示例项目 示例文件夹`example`中有一个非常好的示例项目。过来看。否则,请继续阅读。 ## 定义一个响应式对象 您应该通过如下方式初始化一个新的`Ref`对象: ```dart // 基本类型 var count = Ref(1); var isOk = Ref(true); // 复杂类型-列表 List> list = [ {'title': '苹果', 'price': 23}, ]; var fruitList = Ref(list); // 复杂类型-对象 Map data = { 'info': {'name': '李白', 'age': 12}, }; var poemInfo = Ref(data); // 自定义类型 class User { late int age; User(this.age); } var userInfo = Ref(UserInfo(12)); ``` ## 在Widget中使用 响应式数据如果想刷新widget必须在`RefBuilder`中使用: `RefBuilder`接收一个函数作为参数。首次页面渲染执行这个函数的时候,内部读取到的响应对象会记录这个widget。当下次有任何一个响应对象发生变动,会触发副作用,并更新所有记录的widget. ```dart class TestBase extends StatelessWidget { var count = Ref(20); add() { count.set(() { count.value++; }); } @override Widget build(BuildContext context) { return Container( child: Column( children: [ RefBuilder( () => Text(count.value.toString()), ), TextButton(onPressed: add, child: Text('添加')), ], ), ); } } ``` ## 响应式对象值的获取 要获取响应式对象的值必须通过xxx.value的方式 ```dart var count = Ref(1); print(count.value) // 1 var info = Ref({'name': 'kgm'}); print(count.value['name']); // kgm ``` ## 响应式对象值修改的三种方式 dart是无法监听对象属性的修改的。所以对于复杂类型来说,直接修改响应对象的属性无法触发副作用和widget更新。 请看下面的三种有效方式: - 方式1:适用于基本数据类型值的修改 ```dart // 基本类型 var count = Ref(1); changeCount() { count.value ++ } // 复杂数据类型 var info = Ref({'name': 'kgm'}); changeInfo() { // 下面这句话会触发值的读取,不是重新赋值, info.value['name'] = 'good name'; // 需要添加下面这句话才会触发副作用和widget更新 info.value = info } ``` - 方式2:适用于单个复杂类型响应对象的修改 ```dart var info = Ref({'name': 'kgm'}); changeInfo() { // set方法内部会主动触发副作用和widget更新 // 内部可以返回一个值,当这个值是false的时候,将不会触发副作用和widget更新 info.set(() { info.value['name'] = 'good name'; //return false }) } ``` - 方式3:适用于多个响应式对象的修改 ```dart var info = Ref({'name': 'kgm'}); var fruitList = Ref(['apple', 'orange']); // update是Ref的一个静态方法 // 内部在执行的时候会收集所有的响应式对象,并触发副作用和widget更新 // 内部可以返回一个值,当这个值是false的时候,将不会触发副作用和widget更新 changeData() { // info,fruitList都会触发副作用和widget更新 Ref.update(() { info.value['name'] = 'good name'; fruitList.value.add('banana') //return false }) } // update方法的第二个可选参数:UpdateOptions对象配置如下 /* refs: 指定一组可以触发副作用和widget更新的响应式对象 delay: 可以设置副作用和widget更新的延时时间,这里做了防抖处理 */ refreshInfo() { // 尽管fruitList也发生了修改,但是不会触发副作用和widget更新的响应式对象 // 因为配置了delay属性,相关的副作用和widget更新将会在3s后执行,这有利于做防抖优化。 Ref.update(() { info.value['name'] = 'good name'; fruitList.value.add('banana') }, UpdateOptions( refs:[score], delay: Duration(milliseconds: 3000), ); } ``` ## 定义一个计算属性 您应该通过如下方式初始化一个`RefCompute`对象: `RefCompute` 是 `Ref`的一个子类,但是计算属性自身是无法赋值的。 [注意]如果计算属性定义在widget内部,在widget卸载的时候必须清除依赖! ```dart Map fatherInfo = Ref({ 'info': {'name': 'kgm', 'age': 50}, }); // 下面这个计算属性 // 父亲和儿子年龄相差20岁,根据父亲的年龄来获取儿子的年龄 var childAge = RefCompute(() { return fatherInfo.value['info']['age'] - 20; }); // 可以看到父亲年龄的变动会引起儿子年龄的重新计算 add() { fatherInfo.set(() { fatherInfo.value['info']['age'] += 5; }); } @override void dispose() { // 清除compute依赖 childAge.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( child: Column( children: [ RefBuilder( () => Text( "爸爸年龄:" + fatherInfo.value['info']['age'].toString() ), ), RefBuilder( () => Text( "儿子年龄:" + childAge.value.toString() ), ), TextButton(onPressed: add, child: Text('爸爸老了')), ], ), ); } ``` ## 定义一个事件监听 您想在某一个响应式对象发生变化的时候触发一些额外操作吗? 您应该通过如下方式初始化一个`RefWatch`对象: [注意]如果定义在widget内部,在widget卸载的时候必须清除依赖! ```dart var wacthCount = Ref(1); // 定义一个事件监听 // RefWatch的第一个参数必须返回一个具体的值,否则无法触发副作用 var watchFun = RefWatch(() => wacthCount.value, (oldVal, newVal) { String desc = ''; if (newVal == 0) { desc = '大鸭蛋'; } else if (newVal < 5) { desc = '没及格啊'; } else if (newVal < 8) { desc = '还需继续努力'; } else if (newVal < 10) { desc = 'good'; } score = '上一次$oldVal分,这一次$newVal分。$desc'; print(score); }); add() { if (wacthCount.value < 10) wacthCount.value++; } minis() { if (wacthCount.value > 0) wacthCount.value--; } @override void dispose() { // 清除watch依赖 watchFun.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( child: Column( children: [ RefBuilder( () => Text(wacthCount.value.toString())), TextButton(onPressed: add, child: Text('加分')), TextButton(onPressed: minis, child: Text('减分')), ], ), ); } ``` ## 代理某一个复杂响应式对象的某一个属性 假设这里有一个很复杂的响应式对象:`var ref = Ref({'info': {'name': '老子', 'age': 12},})`。 你会频繁的获取和设置里面的某个属性,如 `Int age = ref.value['info']['age']` 很长很烦恼是不是。那你需要用到`RefKey`这个api了 ```dart var data = { 'info': {'name': '小马猴', 'age': 12}, 'arr': ['苹果', '橘子', '香蕉'] }; var keyInfo = Ref>(data); // 开始代理了------------------------ RefKey arr = RefKey(keyInfo, 'arr.0'); RefKey name = RefKey(keyInfo, 'info.name'); reset() { keyInfo.set(() { name.value = '大马猴'; // keyInfo.value['info']['name'] = '大马猴'; arr.value = '榴莲'; // keyInfo.value['arr'][0] = '榴莲'; }); } @override Widget build(BuildContext context) { return Container( child: Column( children: [ RefBuilder( () => Column( children: [ Text( name.value), // keyInfo.value['info']['name'], Text( arr.value )// keyInfo.value['arr'][0], ], ), ), TextButton(onPressed: reset, child: Text('重置')), ], ), ); } ``` ## 代理某一个复杂响应式对象的所有属性 可以代理普通对象和普通列表的响应式对象, 返回一个Map对象。 [注意]这个api用好不容易。用的不好会给你带来很多烦恼! 看下面的栗子吧! ``` dart List> data = [ {'title': '苹果', 'price': 23}, {'title': '橘子', 'price': 12}, {'title': '香蕉', 'price': 56}, {'title': '西瓜', 'price': 42}, ]; var keysInfo = Ref(data); @override Widget build(BuildContext context) { // 定义到这里,新增的元素不会由任何响应。因为build没有刷新 // var arrList = refKeys>(keysInfo); return Container( child: Column( children: [ Column( children: [ RefBuilder(() { // 定义到这里,新增的元素有响应。因为每次都会生成新的代理对象 var arrList = refKeys>(keysInfo); return Wrap( children: arrList.values.map((ele) { return Row( children: [ if (ele.value['show'] != false) Text(ele.value['title'] ), TextButton( onPressed: () { ele.ref.set(() { ele.value['show'] = ele.value['show'] == false ? true : false; }); }, child: Text(ele.value['show'] != false ? '隐藏' : '显示'), ) ], ); }).toList(), ); }), TextButton( onPressed: () { keysInfo.set(() { keysInfo.value.clear(); }); }, child: Text('清空数组'), ), ], ), ], ), ); } ```