Ref: 这是一个支持flutter数据响应式的组合Api插件
响应式,简单,灵活,可组合,可移植性高,侵入性低。支持数据同步修改,缓存队列异步刷新。
示例文件夹example
中有一个非常好的示例项目。过来看。否则,请继续阅读。
您应该通过如下方式初始化一个新的Ref
对象:
// 基本类型
var count = Ref(1);
var isOk = Ref(true);
// 复杂类型-列表
List<Map<String, dynamic>> list = [
{'title': '苹果', 'price': 23},
];
var fruitList = Ref(list);
// 复杂类型-对象
Map<String, dynamic> data = {
'info': {'name': '李白', 'age': 12},
};
var poemInfo = Ref(data);
// 自定义类型
class User {
late int age;
User(this.age);
}
var userInfo = Ref(UserInfo(12));
响应式数据如果想刷新widget必须在RefBuilder
中使用:
RefBuilder
接收一个函数作为参数。首次页面渲染执行这个函数的时候,内部读取到的响应对象会记录这个widget。当下次有任何一个响应对象发生变动,会触发副作用,并更新所有记录的widget.
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的方式
var count = Ref(1);
print(count.value) // 1
var info = Ref({'name': 'kgm'});
print(count.value['name']); // kgm
dart是无法监听对象属性的修改的。所以对于复杂类型来说,直接修改响应对象的属性无法触发副作用和widget更新。
请看下面的三种有效方式:
// 基本类型
var count = Ref(1);
changeCount() {
count.value ++
}
// 复杂数据类型
var info = Ref({'name': 'kgm'});
changeInfo() {
// 下面这句话会触发值的读取,不是重新赋值,
info.value['name'] = 'good name';
// 需要添加下面这句话才会触发副作用和widget更新
info.value = info
}
var info = Ref({'name': 'kgm'});
changeInfo() {
// set方法内部会主动触发副作用和widget更新
// 内部可以返回一个值,当这个值是false的时候,将不会触发副作用和widget更新
info.set(() {
info.value['name'] = 'good name';
//return false
})
}
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卸载的时候必须清除依赖!
Map<String, dynamic> fatherInfo = Ref({
'info': {'name': 'kgm', 'age': 50},
});
// 下面这个计算属性
// 父亲和儿子年龄相差20岁,根据父亲的年龄来获取儿子的年龄
var childAge = RefCompute<int>(() {
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卸载的时候必须清除依赖!
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了
var data = {
'info': {'name': '小马猴', 'age': 12},
'arr': ['苹果', '橘子', '香蕉']
};
var keyInfo = Ref<Map<String, dynamic>>(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<dynamic, RefKey>对象。
[注意]这个api用好不容易。用的不好会给你带来很多烦恼!
看下面的栗子吧!
List<Map<String, dynamic>> 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<Map<String, dynamic>>(keysInfo);
return Container(
child: Column(
children: [
Column(
children: [
RefBuilder(() {
// 定义到这里,新增的元素有响应。因为每次都会生成新的代理对象
var arrList = refKeys<Map<String, dynamic>>(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('清空数组'),
),
],
),
],
),
);
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。