Flutter BLoC架构下GameBloc触发事件后返回相同DrinkCard列表的问题
1. 未实现GameReloaded事件处理
你的GameBloc仅处理了GamePrepare事件,派发的GameReloaded没有任何对应逻辑,直接被Bloc丢弃,不会重新生成卡牌。
在mapEventToState中补充处理逻辑:
if (event is GameReloaded) { yield GameLoaded( cards: _getCards(event.deck, playerCubit.state), ); }
2. 错误使用未定义变量,且未复用传入的玩家参数
_getCards方法接收了players参数,却调用了未在GameBloc中定义的playerCubit.state,既会报错,也无法获取最新玩家数据。
- 给
GameBloc注入PlayerCubit依赖:
class GameBloc extends Bloc<GameEvent, GameState> { GameBloc({ required this.repository, required this.playerCubit, }) : super(GameInitial()); final GameRepository repository; final PlayerCubit playerCubit;
- 修正
_getCards的参数使用,优先用传入的玩家数据:
List<DrinkCard> _getCards(DeckType deckId, List<Player> players) { final allCards = DeckRepository.getDeckById(deckId).cards; // 不要写playerCubit.state,用传入的players final processedCards = DeckRepository.prepareCards(allCards, players); return processedCards; }
3. 弹窗中创建了独立的GameBloc实例
showModalBottomSheet内的BlocProvider创建了一个全新的GameBloc,和外层显示卡牌的GameBloc完全独立。你在弹窗中派发的所有事件都只会发给新实例,外层的Bloc根本收不到通知,自然不会更新卡牌。
删除弹窗内的多余BlocProvider,直接使用外层已经提供的Bloc实例:
await showModalBottomSheet( context: context, builder: (ctx) { // 移除原来的BlocProvider包裹逻辑 return ModalSheetBody( height: height, controller: _controller, index: _controller.index, callback: () { /* ... */ }, ); }, )
4. 直接修改可变列表,未触发PlayerCubit状态更新
你直接对PlayerCubit.state(Listadd方法,List是引用类型,修改原实例不会触发Cubit的状态通知。哪怕后续派发GameReloaded事件,获取的玩家列表也可能是旧数据。
- 给
PlayerCubit添加专用的修改方法,内部创建新列表再emit:
// PlayerCubit内部实现 void addPlayer(Player player) { emit(List<Player>.from(state)..add(player)); } void removePlayer(String playerName) { emit(List<Player>.from(state)..removeWhere((p) => p.name == playerName)); }
- 替换原来的直接修改逻辑:
// 错误代码:context.read<GameBloc>().playerCubit.state.add(Player(name: name[0])); context.read<PlayerCubit>().addPlayer(Player(name: name[0]));
5. 复用静态卡牌列表引用导致状态判断失效
如果DeckRepository.getDeckById返回的是同一个静态列表实例,且prepareCards直接在原列表上操作,Bloc的状态相等判断(尤其是Equatable对列表的判断)可能会因为引用复用跳过状态更新。
在_getCards中先复制原卡牌列表,避免修改静态源:
List<DrinkCard> _getCards(DeckType deckId, List<Player> players) { // 先复制原列表,生成新的实例 final allCards = List<DrinkCard>.from(DeckRepository.getDeckById(deckId).cards); final processedCards = DeckRepository.prepareCards(allCards, players); return processedCards; }
6. Build阶段派发事件覆盖正常状态
CardStack的BlocBuilder在收到非GameLoaded状态时,会派发使用临时玩家a/b的GamePrepare事件,可能覆盖你后续触发的正常更新事件。
删除CardStack里的自动派发逻辑,改为在应用初始化/Bloc首次创建时触发一次GamePrepare即可,不要在build方法内派发Bloc事件。
内容的提问来源于stack exchange,提问作者Bruno J.

