如何使Flutter中的初始状态不可变

plupiseo  于 2022-12-14  发布在  Flutter
关注(0)|答案(1)|浏览(129)

我想用Flutter写一个小的棋盘游戏。我有关卡的初始状态。简而言之,关卡类包括棋盘类,其中包括许多带有坐标的瓦片。示例如下:

const levels = [
  Level(
    number: 1,
    board: Board(
      cols: 3,
      rows: 1,
      tiles: [
        Tile(
          id: 1,
          isExists: true,
          isFilled: true,
          coordinates: Coordinates(x: 0, y: 0),
        ),
        Tile(
          id: 2,
          isExists: true,
          isFilled: true,
          coordinates: Coordinates(x: 0, y: 1),
        ),
        Tile(
          id: 3,
          isExists: true,
          isFilled: false,
          coordinates: Coordinates(x: 0, y: 2),
        ),
      ],
    ),
  ),
]

**主要问题:**当玩家在游戏场上做出任何动作时,关卡的初始状态会发生变化。特别是“isFilled”字段会发生变化。之后,如果您转到主菜单并再次选择关卡,我们将看到之前发生变化的状态。有人能给我演示一下如何使用ChangeNotifier实现不可变的关卡数组结构吗?

我试着通过ChangeNotifierProvider和Consumer来实现它。也许我需要使用另一个状态管理系统,比如Bloc或类似的东西?我也试着用Level选项复制主对象,但没有帮助。
UPD:简单的addCol函数。
播放会话屏幕组件:

final Board board = Board(
   cols: widget.level.board.cols,
   rows: widget.level.board.rows,
   tiles: widget.level.board.tiles,
   winCondition: widget.level.board.winCondition,
);

return MultiProvider(
   providers: [
     ChangeNotifierProvider(
       create: (context) => BoardState(
         board: board,
       ),
     ),
   ],
   child: Scaffold(
      Consumer<BoardState>(
         builder: (context, boardState, child) => Container(
           child: Column(
            children: [
              Text(boardState.board.cols.toString()),
              InkWell(
              onTap: () =>
                 Provider.of<BoardState>(context, listen: false)
                    .addCol(),
                child: Text('Add col'),
              ),
         ],
       ),
     ),
    ),
   ),
);

主板状态组件:

class BoardState extends ChangeNotifier {
  final Board board;

  BoardState({
    required this.board,
  });

  int get cols => board.cols;

  void addCol() {
    board.cols++;
    notifyListeners();
  }
}

主板类别:

class Board {
  final int cols;

  final int rows;

  final List<Tile> tiles;

  final int winCondition;

  const Board({
    required this.cols,
    required this.rows,
    required this.tiles,
    required this.winCondition,
  });
}

Error Image

class Board {
  late int cols;

  final int rows;

  final List<Tile> tiles;

  final int winCondition;

  Board(
    this.cols, {
    required this.rows,
    required this.tiles,
    required this.winCondition,
  });
}

然后在定义levels常量时出现以下错误:Error 1Error 2

2wnc66cl

2wnc66cl1#

也许我需要使用另一个状态管理系统,像Bloc或类似的东西?
Riverpod是在考虑到不可变性情况下构建的,如果您想给予的话。
也就是说,由于Board被标记为final,所以board.cols++的内容将不起作用。
ChangeNotifier可以很好地处理可变数据。只要让你的Board为非final,并用一个新的Board替换它,其中包含除了更新的cols之外的所有旧属性,如下所示:

class BoardState extends ChangeNotifier {
  Board board;

  BoardState(this.board);

  void addCol() {
    board = Board.copyWith(cols: cols+1);
    notifyListeners();
  }
}

这意味着你的模型需要实现某种copyWith函数。如果你使用@freezed,你可以这样实现:

@freezed
class Board with _$Board{
  const factory Board({
    required int cols,
    required int rows,
    required List<Tile> tiles,
    required int winCondition
  }) = _Board;
}

@freezed
class Tile with _$Tile{
  const factory Tile({
    ... some props
  }) = _Tile;
}

前一个将免费为您提供.copyWith(以及更多)。这应该会消除替换旧数据的痛苦。

相关问题