Flutter Bloc更新状态后不刷新UI的一个解决办法
Flutter 1.12.13+hotfix.8 • channel stable
使用
Equatable创建Bloc
在用Bloc框架写一个项目时,发现在mapEventToState中yield一个state后发现UI并没有刷新,明明改变了关注状态。
究竟是怎么回事呢?

上原代码
1
2
3
4
5
6
7
8
9
class ...
TopicInfoEntity _entity;
...
_entity.data.subscribeStatus = 0;
yield GetTopicDetailState(_entity);
...
Github上搜寻结果发现,问题出在Equatable上
1
2
3
4
5
6
7
8
9
10
11
12
abstract class TopicDetailState extends Equatable {
const TopicDetailState();
}
class GetTopicDetailState extends TopicDetailState {
final TopicInfoEntity infoEntity;
GetTopicDetailState(this.infoEntity);
@override
List<Object> get props => [infoEntity];
}
我们知道,Bloc需要判断一个新state是否需要刷新原UI,需要判断二者state是否相等,如果相等则不刷新。
而我们使用的是需要判断二者是否相等,而Equatable接口便实现的是这个功能。它通过props传入的参数来判断二者是否相等。
那为什么两个有着不同参数的Entity会被判断相等呢?
我们来看Equatable的接口代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@immutable
abstract class Equatable {
List<Object> get props;
bool get stringify => false;
const Equatable();
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Equatable &&
runtimeType == other.runtimeType &&
equals(props, other.props);
@override
int get hashCode => runtimeType.hashCode ^ mapPropsToHashCode(props);
@override
String toString() =>
stringify ? mapPropsToString(runtimeType, props) : '$runtimeType';
}
注意!接口重载了==判断符,判断相同的条件满足以下2点之一即可
identical(this, other),即二者属于相同的Objectother(这里为传入的state)是实现Equatable的,且两个state的运行类型一样,且他们的props相同
经过分析,我们发现上述问题出在props上,于是我们调试进入equals方法。

果不其然,调试过程中,代码一路执行,走到了true
所以二者立刻的相等了,则不刷新UI。
我们来看equals里面判断了什么:
判断List二者不相等的条件可以为:
- 二者至少有一方是
null - props的
List长度不一致 - 二者为
Iterable的,或者为Map(如列表等),同时unit内部的元素也满足前面所述的equals元素(这个equals函数会针对不同数据类型做不同的判断,详情可查看源代码)
其他情况则直接为true
list中有我们的一个entity
- 11行,两个
list不相同,因为每创建一个state,一个空列表都会在state中实例化一次 - 12行,二者都不为
null - …以此类推
我们发现,判断两个entity是否相等,重点在与24行:
在24行判断二者相等时,判断二者相等,因为:
==returns true if two objects are the same instance.
回到我们之前,原来!我们传入state是Bloc中全局变量:相同的_entity,而我们只是改变了其中的一个值而已,两个object还是相等的,因为二者为引用的同一个实例关系。
正确的写法
既然我们的Entity不支持Equatable类型…那我们可以另辟蹊径,新建一个元素,把值copy一遍
好在插件提供了以下方法可以达到copy的效果
1
2
3
4
5
6
7
...
var nEntity = topicInfoEntityFromJson(
TopicInfoEntity(), _entity.toJson());
nEntity.data.subscribeStatus = 0;
yield GetTopicDetailState(nEntity);
_entity = nEntity;
...
改完后就能正常刷新啦~
调试我们也发现正常返回了false

