Flutter Bloc更新狀態後不刷新UI的一個解決辦法
Flutter
1.12.13+hotfix.8
• channel stable
使用Equatable 1.1.1
創建Bloc
在用Bloc框架寫一個項目時,發現在mapEventToState
中yield
一個state
後發現UI
並沒有刷新,明明改變了關注狀態。
究竟是怎麼回事呢?
上原代碼
class ...
TopicInfoEntity _entity;
...
_entity.data.subscribeStatus = 0;
yield GetTopicDetailState(_entity);
...
Github
上搜尋結果發現,問題出在Equatable
上
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
的接口代碼
@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.
回到我們之前,原來!我們傳入state
是Bloc
中全局變量:相同的_entity
,而我們只是改變了其中的一個值而已,兩個object
還是相等的,因爲二者爲引用的同一個實例關係。
正確的寫法
既然我們的Entity
不支持Equatable
類型…那我們可以另闢蹊徑,新建一個元素,把值copy
一遍
好在插件提供了以下方法可以達到copy
的效果
...
var nEntity = topicInfoEntityFromJson(
TopicInfoEntity(), _entity.toJson());
nEntity.data.subscribeStatus = 0;
yield GetTopicDetailState(nEntity);
_entity = nEntity;
...
改完後就能正常刷新啦~
調試我們也發現正常返回了false