Flutter Bloc状态触发后UI未更新问题排查
问题根源分析:Equatable的状态相等判断逻辑
嘿,我正好遇到过类似的问题,咱们来一步步搞清楚为什么会这样:
核心问题:BudgetLoaded状态的相等判断逻辑错了
你的BudgetLoaded类继承了Equatable,但没有重写props方法,这就导致Equatable默认用对象的内存引用来判断两个BudgetLoaded状态是否相等。
咱们还原下Deposit事件的流程:
- 初始状态是
BudgetLoaded(budget: X),这里的X是budgetRepository.budget的引用 - 执行
budgetRepository.budget.deposit(event.amount)时,你是直接修改了X对象的内部属性(比如balance),X的引用还是同一个 - 然后你emit了
new BudgetLoaded(budget: X),这个新状态里的budget和旧状态的budget是同一个引用 - Bloc内部会对比新状态和旧状态是否相等,Equatable一看引用相同,就认为状态没变化,所以不会通知UI更新
那为什么加了emit(BudgetLoading())就有用?
因为这时候状态变化是:BudgetLoaded → BudgetLoading → BudgetLoaded,前两步是不同类型的状态,Bloc会认为状态确实变了,所以会触发UI重建,这时候UI读取的是已经修改过的X对象的属性,就显示更新后的值了。
顺便说下:你的Withdraw其实也有同样的隐患
你可能觉得Withdraw正常,但本质上它和Deposit的逻辑一样:修改同一个budget对象的属性,然后emit同一个引用的BudgetLoaded。如果后续UI依赖更复杂的状态变化,Withdraw也会出现不更新的情况,只是你目前没碰到而已。
正确的解决方案
要从根本上解决这个问题,需要遵循Bloc的不可变状态设计原则,同时正确配置Equatable的props:
- 给
BudgetLoaded重写props方法,让它基于budget的内容判断相等性:
class BudgetLoaded extends BudgetState { final Budget budget; BudgetLoaded({required this.budget}); @override List<Object> get props => [budget]; // 把budget加入props判断 }
- 让
Budget类也继承Equatable,并重写props,同时把修改方法改成返回新的Budget对象(不可变模式):
class Budget extends Equatable { final double balance; const Budget({required this.balance}); // deposit返回新对象,不修改原对象 Budget deposit(double amount) { return Budget(balance: balance + amount); } // withdraw同理 Budget withdraw(double amount) { return Budget(balance: balance - amount); } @override List<Object> get props => [balance]; // 基于balance判断相等 // 你的numberFormatted方法... String get numberFormatted => "\$${balance.toStringAsFixed(2)}"; }
- 最后修改Bloc里的事件处理,更新repository的budget为新对象:
on<Deposit>((event, emit) { // 生成新的budget对象,替换旧的 final newBudget = budgetRepository.budget.deposit(event.amount); budgetRepository.budget = newBudget; emit(BudgetLoaded(budget: newBudget)); }); on<Withdraw>((event, emit) { final newBudget = budgetRepository.budget.withdraw(event.amount); budgetRepository.budget = newBudget; emit(BudgetLoaded(budget: newBudget)); });
这样修改后,每次Deposit/Withdraw都会生成新的Budget对象,新的BudgetLoaded状态和旧状态的props(budget的内容)不同,Bloc就会正确识别状态变化,触发UI更新,再也不需要先emit Loading状态了。
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

