0

I want to write a small board game in Flutter. I have the initial state of the levels. In short, the Level class includes the Board class, which includes many tiles with coordinates. Example below:

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),
        ),
      ],
    ),
  ),
]

The main problem: When a player makes any actions on the playing field, the initial state of the levels changes. In particular, the "isFilled" field changes. After that, if you go to the main menu and select the level again, we will see the previously changed state. Can someone please show me an example how to implement immutable array of levels construct using ChangeNotifier?

I tried to implement it through ChangeNotifierProvider and Consumer. Maybe i need to use another state management system, like Bloc or something like that? Also i tried to copy the main object with Level options, but it didn't help.

UPD: Simple addCol function.

PlaySessionScreen Component:

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'),
              ),
         ],
       ),
     ),
    ),
   ),
);

BoardState Component:

class BoardState extends ChangeNotifier {
  final Board board;

  BoardState({
    required this.board,
  });

  int get cols => board.cols;

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

Board Class:

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,
  });
}

Then I get the following errors when defining the levels constant: Error 1Error 2

pawaebawy
  • 1
  • 1

1 Answers1

0

Maybe i need to use another state management system, like Bloc or something like that?

Riverpod is build with immutability in mind, if you want to give it a go.

That said, stuff board.cols++ won't work since Board is marked as final.

ChangeNotifier works (well) with mutable data. Just let your Board be non-final and replace it with a new Board with all the old props except the updated cols, like so:

class BoardState extends ChangeNotifier {
  Board board;

  BoardState(this.board);

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

This implies that your model needs to implement some sort of copyWith function. If you're using @freezed you could achieve that like this:

@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;
}

The previous will give you .copyWith (and more) for free. This should smooth out the pain of substituting old data.

venir
  • 1,809
  • 7
  • 19