This document explains the workflow and architecture of the XO Game application, detailing how different components interact with each other.
The application follows a Provider-based state management pattern with a clear separation of concerns:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ │ │ │ │
│ Providers │◄────►│ Screens │◄────►│ Widgets │
│ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
▲ ▲
│ │
│ ┌─────────────────┐ │
│ │ │ │
└───────────────►│ Theme & Utils │◄─────────────┘
│ │
└─────────────────┘
-
Entry Point (
main.dart):- Initializes Flutter binding
- Sets up window configuration for desktop platforms
- Initializes SharedPreferences for persistent storage
- Creates providers (GameState and ThemeProvider)
- Runs the app with providers at the root
-
Theme Setup:
ThemeProviderreads saved theme preference- Appropriate theme (light/dark) is applied to the MaterialApp
The game logic is centralized in the GameState class which extends ChangeNotifier:
┌───────────────────────────────────────┐
│ GameState │
├───────────────────────────────────────┤
│ - _board: List<String> │
│ - _currentPlayer: String │
│ - _gameOver: bool │
│ - _winner: String? │
│ - _winningLine: List<int>? │
│ - _scores: Map<String, int> │
├───────────────────────────────────────┤
│ + makeMove(index): void │
│ + resetGame(): void │
│ + resetScores(): void │
│ + getGameStatus(): String │
│ + getCellValue(index): String │
└───────────────────────────────────────┘
-
Game Initialization:
GameStateinitializes with an empty board and 'X' as the starting player- Scores are loaded from SharedPreferences
-
Player Move:
- User taps a cell, triggering
onTapcallback GameState.makeMove(index)is called- Cell is marked with current player's symbol ('X' or 'O')
- Sound effect plays and haptic feedback triggers
- Game checks for a winner or draw condition
- User taps a cell, triggering
-
Winner Detection:
_checkWinner()method checks all possible winning combinations- If a winning combination is found:
- Winner is set
- Winning line indices are stored
- Game is marked as over
- Score is incremented and saved
- Win sound and haptic feedback are triggered
- If all cells are filled with no winner:
- Game is marked as over with no winner (draw)
- Draw sound and haptic feedback are triggered
-
Game Reset:
- User taps "New Game" button
- Board is cleared, game state is reset
- Player 'X' becomes active again
-
Theme Toggle:
- User taps theme toggle icon
- Theme preference is toggled and saved
- UI updates to reflect the new theme
Updates to the UI follow a reactive pattern through Provider:
- Changes to game state trigger
notifyListeners() - Widget tree rebuilds where
context.watch<GameState>()is used - UI reflects the current state of the game
The game features several animations:
AnimatedGameCelllistens for changes to its value- When value changes from empty to a player symbol:
- Scale animation starts from 0.5 to 1.0 with elastic curve
- Opacity animation fades in the symbol
- When
winningLineis set in GameState:WinningLinePainterdraws a line connecting the winning cells- Animation progresses from 0.0 to 1.0, extending the line across the winning cells
-
Game Scores:
- Scores are saved to SharedPreferences when updated
- Loaded when the app starts
-
Theme Preference:
- Theme setting (light/dark) is saved when changed
- Loaded when the app starts
-
Platform Detection:
PlatformUtilsdetects the current platform- UI adapts based on platform (desktop vs mobile)
-
Desktop Window Management:
- For desktop platforms,
WindowUtilsconfigures the window size and behavior
- For desktop platforms,
-
Sound Effects:
- Sound effects are tied to game events (move, win, draw, reset)
- Controlled by the
_soundEnabledpreference
-
Haptic Feedback:
- Different vibration patterns for different events
- Only active on devices with vibration capability
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ GameScreen │ │ GameState │ │ ThemeProvider │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ Displays UI │ │ Manages logic │ │ Manages themes │
│ Handles input │◄───►│ Updates state │ │ Provides colors │
│ Shows animations│ │ Triggers sounds │◄───►│ Saves preference│
└─────────────────┘ └─────────────────┘ └─────────────────┘
▲ ▲ ▲
│ │ │
▼ │ │
┌─────────────────┐ │ │
│ UI Components │ │ │
├─────────────────┤ │ │
│ AnimatedGameCell│◄────────────┘ │
│ WinningLinePaint│◄────────────────────────────────────┘
└─────────────────┘
-
To test different game scenarios:
- Modify the
_boardarray directly in debug mode - Call
_checkWinner()to validate win detection - Test persistence by closing and reopening the app
- Modify the
-
To test different platforms:
- Run on mobile:
flutter run -d androidorflutter run -d ios - Run on desktop:
flutter run -d windows|macos|linux
- Run on mobile:
-
Development Build:
flutter build apk --debug flutter build ios --debug flutter build windows --debug
-
Production Build:
flutter build apk --release flutter build appbundle --release flutter build ios --release flutter build windows --release flutter build macos --release flutter build linux --release
The architecture allows for easy extension in several areas:
-
New Game Features:
- Add AI opponent by extending GameState
- Add multiplayer by modifying GameState to handle remote moves
- Add game variations by changing the win condition logic
-
UI Enhancements:
- Add more themes by extending AppTheme
- Add animations by creating new AnimatedWidgets
- Add customization options in the settings dialog
-
Animation Performance:
- Use of
AnimationControllerwith vsync for efficient animations - Custom painters for drawing winning lines instead of widget overlays
- Use of
-
State Management:
- Selective rebuilds with Provider to minimize UI updates
- Game logic isolated from UI for better testability and performance