Flutter 多个obs状态绑定同一个Widget异步刷新的解决方法
今天修改flutter的老项目时发现,如果同一个组件使用两个不同的obs来刷新状态时,在某些情况下会出现体验不好的效果。
例子
举一个简单的例子好了
代码
比如说我要控制 TextField
的两个状态,一个obs控制上面的文字,另一个obs控制TextField
加载完成后是否自动聚焦。
所以我会定义两个 obs 变量
final title = "默认".obs;
final autofocus = false.obs; // 默认为不自动聚焦
调用组件位置:
Obx(() =>
// TextField 无法直接设置 title 值,这里做一个封装
CustomTextField(
text : text.value,
autofocus : autofocus.value,
onSubmit : (text) {
// 这里是 TextField 提交回调
// 此时要不自动唤起键盘
autofocus.value = false;
// TextField 提交时会修改 title 的值从而令这里的 text 刷新变化
title.value = text;
}
)
);
这时只要title
和autofocus
中其中一个改变都会引起CustomTextField
的刷新。
现在有一个接口:
void setAutofocus(bool isAutofocus) {
autofocus.value = isAutofocus;
}
预期效果
调用 setAutofocus
设置为 true
刷新CustomTextField
会自动唤起键盘,然后输入修改,修改完成后,刷新修改后的值,并且回到只读状态。
问题
好像没什么问题对吧,我们细看一下这个回调
autofocus.value = false;
后异步刷新,然后不调用键盘。
title.value = text;
后异步刷新这个控件,然后正确显示文字。
autofocus
刷新控件时,由于title
还没更新,导致在获取文字时得到了旧值,此时显示的文字会瞬间回退到上一版本,然后等待title
刷新控件时,才会显示正确的结果。虽然仅仅只是一瞬间,但很明显,达不到预期效果。
然后试着调换两行代码,
此时虽然文字不再闪现了,但由于title
刷新控件时,autofocus
是旧值,即true
导致键盘闪现升起然后降下,又寄了。
解决方法
其实主要的问题就是,在刷新时无法获取到新值。
那么我们就可以创建一个不会导致控件刷新的正常变量来一起控制控件的状态,如:
bool editing = false; // 默认不在编写状态
控件的autofocus
设置成autofocus.value && editing
然后我们在setAutofocus
为true
的时候,将editing
也设置为true
这时刷新控件,autofocus
属性为true
修改完成后,先将editing
修改为false
,不会导致控件刷新。
先提交结果,文字修改,由于autofocus
属性此时判断为false
就不会导致键盘被唤醒。
然后再调用autofocus.value = false;
会自动刷新状态,但由于editing
已经为false
,所以状态其实没有变动。
如果遇到更新文字比较慢的时候(如需要更新数据库),可以使用定时器将autofocus.value = false;
延后一点运行。
总结
解决方法其实就是用一个变量来缓存此时的结果,提供给第一次刷新变量的控件。