Flutter Bloc更新状态后不刷新UI的一个解决办法

Flutter

Posted by MetaNetworks on March 31, 2020
本页面总访问量

Flutter Bloc更新状态后不刷新UI的一个解决办法

Flutter 1.12.13+hotfix.8 • channel stable

使用Equatable创建Bloc

在用Bloc框架写一个项目时,发现在mapEventToStateyield一个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),即二者属于相同的Object
  • other(这里为传入的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.

回到我们之前,原来!我们传入stateBloc中全局变量:相同的_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