You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Bloc状态触发后UI未更新问题排查

问题根源分析:Equatable的状态相等判断逻辑

嘿,我正好遇到过类似的问题,咱们来一步步搞清楚为什么会这样:

核心问题:BudgetLoaded状态的相等判断逻辑错了

你的BudgetLoaded类继承了Equatable,但没有重写props方法,这就导致Equatable默认用对象的内存引用来判断两个BudgetLoaded状态是否相等。

咱们还原下Deposit事件的流程:

  1. 初始状态是BudgetLoaded(budget: X),这里的X是budgetRepository.budget的引用
  2. 执行budgetRepository.budget.deposit(event.amount)时,你是直接修改了X对象的内部属性(比如balance),X的引用还是同一个
  3. 然后你emit了new BudgetLoaded(budget: X),这个新状态里的budget和旧状态的budget是同一个引用
  4. Bloc内部会对比新状态和旧状态是否相等,Equatable一看引用相同,就认为状态没变化,所以不会通知UI更新

那为什么加了emit(BudgetLoading())就有用?
因为这时候状态变化是:BudgetLoaded → BudgetLoading → BudgetLoaded,前两步是不同类型的状态,Bloc会认为状态确实变了,所以会触发UI重建,这时候UI读取的是已经修改过的X对象的属性,就显示更新后的值了。

顺便说下:你的Withdraw其实也有同样的隐患

你可能觉得Withdraw正常,但本质上它和Deposit的逻辑一样:修改同一个budget对象的属性,然后emit同一个引用的BudgetLoaded。如果后续UI依赖更复杂的状态变化,Withdraw也会出现不更新的情况,只是你目前没碰到而已。

正确的解决方案

要从根本上解决这个问题,需要遵循Bloc的不可变状态设计原则,同时正确配置Equatable的props:

  1. 给BudgetLoaded重写props方法,让它基于budget的内容判断相等性:
class BudgetLoaded extends BudgetState {
  final Budget budget;

  BudgetLoaded({required this.budget});

  @override
  List<Object> get props => [budget]; // 把budget加入props判断
}
  1. 让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)}";
}
  1. 最后修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 20:19:06