Creating Full Chess Game Using Flutter

By Mohammed Yaseen, 18 min read, May 09 2026

Creating Full Chess Game Using Flutter

Chess is one of the best projects for learning game development because it teaches logic, state management, animations, user interaction, and multiplayer systems together in a single project. When I first started building games with Flutter, I focused mostly on arcade projects and small browser games. Later I wanted to challenge myself with something more complex. That was when I decided to build a full chess game.

At first the project looked very difficult. Chess contains many rules, different piece movements, special conditions, turns, captures, check detection, and game ending systems. But once I divided the project into smaller parts, the development process became much easier.

Flutter works very well for chess games because the interface system is flexible and responsive. The board can adapt easily across phones, tablets, desktops, and browsers. Flutter animations also help pieces move smoothly while keeping the project lightweight.

The first thing I created was the board itself. A chess board contains eight rows and eight columns. Every square alternates between light and dark colors. I used GridView to generate the board dynamically instead of manually creating sixty four containers.

GridView.builder( itemCount: 64, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 8, ), itemBuilder: (context, index) { int row = index ~/ 8; int col = index % 8; bool isWhite = (row + col) % 2 == 0; return Container( color: isWhite ? Colors.brown.shade200 : Colors.brown.shade700, ); }, )

This small system creates the full board automatically. The row and column calculations help identify every square position. Once the board existed visually, the next challenge was placing pieces.

I created a chess piece model so every piece could store its own information. Models are extremely important in game development because they organize data properly. Without models, projects become messy very quickly.

class ChessPiece { final String type; final bool isWhite; final String image; ChessPiece({ required this.type, required this.isWhite, required this.image, }); }

This model stores the piece type, team color, and image path. After creating the model, I built the starting board layout using a two dimensional list.

List<List<ChessPiece?>> board = List.generate( 8, (index) => List.generate(8, (index) => null), );

The question mark is important because some squares are empty. Null safety helps prevent crashes during gameplay. Earlier versions of Dart allowed accidental null values which caused many runtime problems. Modern Flutter development is much safer because null handling is built directly into the language.

After creating the board structure, I initialized all pieces in their starting positions.

void initializeBoard() { board[0][0] = ChessPiece( type: "rook", isWhite: false, image: "assets/black_rook.png", ); board[0][1] = ChessPiece( type: "knight", isWhite: false, image: "assets/black_knight.png", ); board[7][4] = ChessPiece( type: "king", isWhite: true, image: "assets/white_king.png", ); }

I repeated this process for every piece on the board. Once all pieces appeared correctly, the game finally started looking real.

The next major challenge was movement logic. Every chess piece moves differently. Pawns move forward. Bishops move diagonally. Knights jump in L shapes. Rooks move horizontally and vertically. Queens combine rook and bishop movement. Kings move one tile at a time.

I created separate movement validation systems for every piece type. This made debugging much easier later.

bool isValidPawnMove( int startRow, int startCol, int endRow, int endCol, bool isWhite, ) { int direction = isWhite ? -1 : 1; if (startCol == endCol && endRow == startRow + direction && board[endRow][endCol] == null) { return true; } return false; }

This logic handles simple pawn movement. Later I expanded it for double movement and diagonal captures.

Building movement systems taught me an important lesson about game development. Complex games become manageable when features are developed one at a time. Trying to build everything together creates confusion quickly.

After movement validation worked, I added user interaction. Players needed to tap pieces, select destinations, and move them smoothly across the board.

GestureDetector( onTap: () { setState(() { selectedRow = row; selectedCol = col; }); }, child: Container( child: piece != null ? Image.asset(piece.image) : null, ), )

The selected piece coordinates are stored whenever the user taps a square. After that, another tap attempts movement validation.

One thing I enjoyed while creating chess was how visual the debugging process became. Whenever movement rules failed, I could immediately see problems directly on the board. This made testing easier compared to invisible backend systems.

The next feature was turn management. Chess alternates between white and black players. Without turn restrictions, users could move any piece anytime.

bool whiteTurn = true; void switchTurn() { whiteTurn = !whiteTurn; }

Before allowing movement, I checked whether the selected piece belonged to the active player.

if (piece.isWhite != whiteTurn) { return; }

This simple condition prevents illegal moves instantly.

After turn systems worked correctly, I focused on captures. Capturing pieces adds excitement because the board changes constantly during gameplay.

void movePiece( int startRow, int startCol, int endRow, int endCol, ) { board[endRow][endCol] = board[startRow][startCol]; board[startRow][startCol] = null; }

Since the destination piece gets replaced automatically, captures happen naturally inside the same movement system.

Later I added animations because instant teleportation felt unnatural. Flutter animation widgets improved the overall polish significantly.

One of the hardest systems was check detection. The game must constantly verify whether kings are under attack. This requires scanning every enemy movement possibility after each turn.

bool isKingInCheck(bool whiteKing) { for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { ChessPiece? piece = board[row][col]; if (piece == null) continue; if (piece.isWhite == whiteKing) continue; } } return false; }

This system later expanded into full attack scanning logic. Checkmate detection became even more difficult because the game must verify whether escape moves exist.

At this stage the project transformed from a simple board into a real strategy game. The deeper I went into chess development, the more I understood how much logic exists behind classic games.

Single player mode became my next focus. I wanted players to compete against the computer even without internet access. Creating a perfect chess AI is extremely difficult, so I started with simple random movement systems.

void makeComputerMove() { List<Move> possibleMoves = []; for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { ChessPiece? piece = board[row][col]; if (piece == null) continue; if (piece.isWhite) continue; } } }

The computer searches for possible black moves and randomly selects one. Even basic AI creates surprisingly fun gameplay for beginners.

Later I improved the AI by prioritizing captures and avoiding dangerous moves. Strong chess engines require advanced search algorithms like minimax and alpha beta pruning, but beginner projects can still feel enjoyable using simpler logic.

Offline multiplayer became another important feature. Many players enjoy chess with friends sitting nearby on the same device. This mode is much easier than online multiplayer because both users share the same screen.

Since the turn system already existed, offline multiplayer worked naturally. Players simply alternate turns physically.

Then I experimented with online multiplayer. This introduced networking challenges for the first time. Synchronizing board states between devices requires backend communication.

Firebase became extremely useful here because it handles realtime updates efficiently. Whenever one player moves a piece, the board state updates inside the database and synchronizes instantly across devices.

FirebaseFirestore.instance .collection("matches") .doc(matchId) .set({ "board": boardData, "whiteTurn": whiteTurn, });

Listening for updates is equally important because both players must receive movement changes immediately.

FirebaseFirestore.instance .collection("matches") .doc(matchId) .snapshots() .listen((snapshot) { final data = snapshot.data(); });

Multiplayer development taught me how important synchronization is in games. Even tiny delays can confuse players badly.

Another challenge appeared when handling disconnects. Sometimes users close the app suddenly or lose internet connection. The game should not crash during these situations.

I added status tracking so matches could detect whether opponents disconnected.

{ "player1Online": true, "player2Online": false }

Timer systems also became important because chess games often include countdown clocks. Flutter timers made this relatively simple.

Timer.periodic( const Duration(seconds: 1), (timer) { if (whiteTurn) { whiteTime--; } else { blackTime--; } }, );

Timers add pressure and excitement to matches. Blitz chess especially becomes very intense.

Sound effects improved the atmosphere significantly too. Simple movement sounds and capture sounds made the game feel alive.

I also learned that responsive design matters heavily in board games. Small phones need different scaling compared to desktop screens.

double boardSize = MediaQuery.of(context).size.width;

Responsive calculations help maintain proper square sizes across all devices.

Saving game progress became another useful feature. Some matches last very long, especially in multiplayer mode. Players should continue later without losing progress.

SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString( "savedBoard", jsonEncode(boardData), );

This allows local save functionality without requiring internet.

Performance optimization also matters even in chess games. Large rebuilds can create lag during animations. I reduced unnecessary widget rebuilds using smaller components and efficient state updates.

During development I made many mistakes. Some movement rules broke unexpectedly. Check detection failed during edge cases. Multiplayer synchronization occasionally duplicated moves. But every problem improved my understanding of game systems.

One feature I enjoyed adding was move highlighting. When players select a piece, the game displays possible movement squares visually.

Container( decoration: BoxDecoration( color: isPossibleMove ? Colors.green.withOpacity(0.3) : Colors.transparent, ), )

Visual feedback improves usability massively, especially for beginners learning chess.

Pawn promotion became another interesting mechanic. When pawns reach the opposite side, players choose replacement pieces.

if (piece.type == "pawn" && endRow == 0) { board[endRow][endCol] = ChessPiece( type: "queen", isWhite: true, image: "assets/white_queen.png", ); }

Castling and en passant required even more special rules. These systems reminded me how detailed classic board games actually are.

Once the game became stable, I focused more on polish. Menus, victory screens, matchmaking interfaces, loading indicators, and animations improved the professional feel.

Flutter works especially well for menu systems because interface creation is extremely flexible. Combining Flutter UI with game logic creates powerful cross platform experiences.

I also tested the game on browser builds. Flutter web handled the chess project surprisingly well because chess does not require heavy rendering like large action games.

Keyboard shortcuts improved desktop gameplay further.

Focus( autofocus: true, onKeyEvent: (node, event) { return KeyEventResult.handled; }, child: Scaffold( body: Container(), ), )

Accessibility matters too. Clear contrast, readable text, and responsive controls help more players enjoy the experience comfortably.

Looking back, chess became one of the most educational projects I ever built. It combines algorithms, user interaction, animations, networking, state management, optimization, and responsive design together in a single experience.

For beginners, my advice is simple. Do not fear large projects. Break systems into smaller sections and solve one problem at a time. Start with the board. Then movement. Then turns. Then captures. Eventually the project grows naturally.

Building a full chess game using Flutter teaches far more than just chess mechanics. It teaches patience, architecture, debugging, planning, and long term thinking. Every feature adds another layer of understanding.

Today cross platform game development is more accessible than ever. Flutter allows developers to target phones, browsers, desktops, and tablets using one codebase. A chess project becomes a perfect learning experience because it remains lightweight while still containing advanced logic.

At the end of the journey, creating a complete chess game feels incredibly rewarding. Watching pieces move smoothly across the board, seeing multiplayer matches synchronize correctly, and knowing the entire system works because of your own code creates a special feeling that every game developer should experience at least once.