Flutter Bloc中布尔状态变量首次赋值后停止变更的问题排查
Flutter Bloc计时器应用isBreak值无法正常切换问题
我是Flutter新手,正在用Bloc开发一款交替进行工作和休息时段的计时器应用,通过SessionsBloc中的布尔值isBreak跟踪当前时段类型。预期每次触发SessionTrackingEvent时,isBreak能切换为相反值,但实际只有第一次从false变为true生效,之后不再变化。尝试过修改变量类型、调整状态读取逻辑,但问题依旧。
代码片段
1. SessionsState定义
part of 'sessions_bloc.dart'; abstract class SessionsState extends Equatable { final bool isBreak; final int sessionCount; const SessionsState(this.isBreak, this.sessionCount); @override List<Object> get props => []; } class SessionsInitial extends SessionsState { const SessionsInitial(super.isBreak, super.sessionCount); } class SessionTrackingState extends SessionsState { const SessionTrackingState(super.isBreak, super.sessionCount); }
2. BlocListener监听逻辑
listener: (context, state) { Task currentTask = BlocProvider.of<TasksBloc>(context).state.currentTask; bool isTimeBoxed = currentTask.isTimeBoxed; int sessionDuration; int breakDuration; if (state is TimerCompleteState) { // 获取SessionsBloc当前状态 final sessionState = BlocProvider.of<SessionsBloc>(context).state; bool isBreak = sessionState.isBreak; int sessionCount = sessionState.sessionCount; print(sessionState.isBreak); print(sessionState.sessionCount); if (isTimeBoxed) { sessionDuration = currentTask.sessionTimeBox!; breakDuration = currentTask.breakTimeBox ?? 2; // sessionCount = HiveDb().getTaskSessionCount(currentTask.taskName); } else { sessionDuration = 5; breakDuration = 3; } if (isBreak) { BlocProvider.of<TimerBloc>(context) .add(InitializeTimerEvent(duration: sessionDuration)); BlocProvider.of<SessionsBloc>(context).add(SessionTrackingEvent( isBreak: isBreak, sessionCount: sessionCount, )); } else { BlocProvider.of<TimerBloc>(context) .add(InitializeTimerEvent(duration: breakDuration)); BlocProvider.of<TimerBloc>(context) .add(OnBreakEvent(duration: breakDuration)); BlocProvider.of<SessionsBloc>(context).add(SessionTrackingEvent( isBreak: isBreak, sessionCount: sessionCount += 1, )); } } },
3. SessionsBloc实现
class SessionsBloc extends Bloc<SessionsEvent, SessionsState> { SessionsBloc() : super(const SessionsInitial(false, 0)) { on<SessionTrackingEvent>((event, emit) { emit(SessionTrackingState( event.isBreak ? false : true, event.sessionCount)); }); } }
问题根源
Equatable未正确重写props:
SessionsState继承了Equatable,但props返回空列表,导致Bloc无法判断状态是否发生变化。当emit新状态时,Bloc会对比新旧状态的props,若完全一致则不会触发UI更新,后续读取状态也无法获取最新值。事件处理依赖旧值而非当前状态:当前
SessionTrackingEvent携带的是触发事件时读取的旧isBreak值,SessionsBloc处理事件时直接基于这个旧值切换。这种依赖外部传递参数的方式容易导致状态不同步,正确的做法应该是让Bloc基于自身当前状态计算新值。
修复步骤
步骤1:修正Equatable的props
修改SessionsState的props,将isBreak和sessionCount加入列表,确保Bloc能正确识别状态变化:
abstract class SessionsState extends Equatable { final bool isBreak; final int sessionCount; const SessionsState(this.isBreak, this.sessionCount); @override List<Object> get props => [isBreak, sessionCount]; // 加入状态字段 }
步骤2:优化SessionsBloc的事件处理逻辑
简化SessionTrackingEvent(不需要传递参数,让Bloc自行维护状态):
class SessionTrackingEvent extends SessionsEvent { const SessionTrackingEvent(); }
修改SessionsBloc的事件处理逻辑,基于当前状态计算新值:
class SessionsBloc extends Bloc<SessionsEvent, SessionsState> { SessionsBloc() : super(const SessionsInitial(false, 0)) { on<SessionTrackingEvent>((event, emit) { // 获取当前状态的字段值 final currentIsBreak = state.isBreak; final currentSessionCount = state.sessionCount; // 切换isBreak,非休息时段则sessionCount+1 final newIsBreak = !currentIsBreak; final newSessionCount = currentIsBreak ? currentSessionCount : currentSessionCount + 1; emit(SessionTrackingState(newIsBreak, newSessionCount)); }); } }
步骤3:调整BlocListener中的事件触发
因为事件不再需要传递参数,修改listener中的调用:
// 替换原来带参数的调用 BlocProvider.of<SessionsBloc>(context).add(const SessionTrackingEvent());
内容的提问来源于stack exchange,提问作者Murithi Vin
相关产品推荐
相关产品推荐

