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

Flutter中emit新状态时BlocBuilder不重建Widget问题排查

井字棋Cubit状态更新后BlocConsumer不重建的问题解决

问题概述

开发井字棋学习Bloc/Cubit状态管理时,玩家点击格子后棋盘无法渲染更新,调用cubit.makePlay触发emit新状态,但BlocConsumer的builder方法不执行,界面无变化。

核心问题与修复方案

1. GameState未正确实现Equatable的props对比

GameState继承自Equatable,但props返回空列表,导致Equatable无法判断状态是否发生变化,Bloc会认为新状态与旧状态相同,因此不触发UI重建。

修复代码:
修改GameState中的props,包含所有需要参与状态对比的字段:

@override
List<Object?> get props => [
  status,
  playerTurn,
  drawGameScore,
  oPlayerScore,
  xPlayerScore,
  board,
  gameWinner,
];

2. _hasWinner与_hasDraw方法使用旧状态判断

当前_hasWinner和_hasDraw直接使用state.board判断,但此时已经生成了新的棋盘actualBoard,判断逻辑会基于旧棋盘执行,导致胜负判断错误。

修复代码:
将两个方法改为接收新棋盘参数:

bool _hasWinner(List<String> board) {
  if (board[0] != '' &&
          board[0] == board[1] &&
          board[0] == board[2] ||
      board[3] != '' &&
          board[3] == board[4] &&
          board[3] == board[5] ||
      board[6] != '' &&
          board[6] == board[7] &&
          board[6] == board[8] ||
      board[0] != '' &&
          board[0] == board[4] &&
          board[0] == board[8] ||
      board[2] != '' &&
          board[2] == board[6] &&
          board[2] == board[4] ||
      board[0] != '' &&
          board[0] == board[3] &&
          board[0] == board[6] ||
      board[1] != '' &&
          board[1] == board[4] &&
          board[1] == board[7] ||
      board[2] != '' &&
          board[2] == board[5] &&
          board[2] == board[8]) {
    return true;
  }
  return false;
}

bool _hasDraw(List<String> board) => !board.contains('');

在makePlay中调用时传入新棋盘:

if (_hasWinner(actualBoard)) {
  // ... 状态发射逻辑
} else if (_hasDraw(actualBoard)) {
  // ... 状态发射逻辑
}

3. 获胜/平局状态被立即重置覆盖

当前makePlay中emit获胜/平局状态后,立即调用resetBoard发射重置状态,导致UI还未响应胜负状态就被重置。需将重置逻辑移到弹窗关闭时执行。

修复代码:
移除makePlay中获胜/平局分支的resetBoard()调用,修改_showWinDialog的按钮逻辑:

void _showWinDialog({
  required BuildContext context,
  required GameCubit cubit,
  required String winner,
}) {
  cubit.increaseWinScore(winner: winner);
  showDialog(
    barrierDismissible: false,
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text(' $winner  venceu!'),
        actions: [
          TextButton(
            child: const Text("Jogar novamente"),
            onPressed: () {
              cubit.resetBoard();
              Navigator.of(context).pop();
            },
          )
        ],
      );
    },
  );
}

同时确保emit胜负状态时更新棋盘:

emit(
  state.copyWith(
    status: GameStatus.hasWinner,
    gameWinner: playerTurn,
    playerTurn: revertPlayerTurn(actualPlayer: playerTurn),
    board: actualBoard, // 新增棋盘更新
  ),
);

4. increaseWinScore方法的得分更新错误

当前increaseWinScore的else分支错误更新了xPlayerScore,需修正为oPlayerScore:

void increaseWinScore({required String winner}) {
  if (winner == 'X') {
    emit(state.copyWith(xPlayerScore: state.xPlayerScore + 1));
  } else {
    emit(state.copyWith(oPlayerScore: state.oPlayerScore + 1));
  }
}

并在胜负弹窗中调用该方法更新得分。

内容的提问来源于stack exchange,提问作者Marcelo Arthur

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 18:42:05