Horje
Building a Simple Tic Tac Toe Game in Flutter

Tic Tac Toe is a two-player game that anybody of any age can play and understand are rules in a matter of minutes. In this tutorial, we are going to show you how to create a simple Tic Tac Toe game in Flutter step by step. I tried my best to make this guide easy to understand and preferably for new users who are unfamiliar with Flutter or programming. Once at the end of the video, you will have a complete fully working Tic Tac Toe game!

Tic Tac Toe Game

Another game frequently played by two people is Tic Tac Toe or Noughts and Crosses as it is also called. The aim of the game is to be the first in getting your marks in a row; either parallel to the surface of the game, or perpendicular to the surface of the game or any other direction you try. We will be building a basic version of this game in Flutter in this tutorial.

Implementation of Simple Tic Tac Toe

Step 1: Create a New Flutter Project

Before going any further, ensure that you have Flutter up and running. If it has not been installed already, here you will follow the official Flutter installation process. Once you have Flutter installed, create a new project by running the following commands in your terminal:

flutter create tic_tac_toe
cd tic_tac_toe

Open the project in your favourite code editor (like VS Code or Android Studio).

Directory Structure

gfg

Folder structure


Step 2: Set Up the Main Structure

Open the main.dart file and replace its content with the following code to set up the basic structure of the Flutter app:

main.dart
import 'package:flutter/material.dart';

// Main Function
void main() {
  runApp(const MyApp());
}

// MyApp class
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Tic Tac Toe',
      debugShowCheckedModeBanner: false,
      home: TicTacToePage(),
    );
  }
}


This sets up the main structure of our Flutter app with a title and a home page.


Step 3: Creating the Game Page

Now, it is time to design the Tic Tac Toe game page.

First of all, let’s start writing a stateful widget for the Tic Tac Toe game. This will enable us control the state of the game, in order to have the best chance of emerging as the ultimate winner.

APPEND the following code to the main below the MyApp class. dart file:

Dart
class TicTacToePage extends StatefulWidget {
  const TicTacToePage({super.key});
  @override
  State<TicTacToePage> createState() => _TicTacToePageState();
}

Defining the State

Next, we define the state for the TicTacToePage. This includes initializing the game board, keeping track of the current move, and a counter for the number of moves.

Dart
class _TicTacToePageState extends State<TicTacToePage> {
  List<String> moves = List.filled(9, "-");
  String currentMove = "X";
  int count = 0;


Building the Scaffold

Now, we build the scaffold for the game page. This includes an AppBar and the main content area.

Dart
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Tic Tac Toe'),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "Current Move: $currentMove",
              style: const TextStyle(fontSize: 25),
            ),
            const SizedBox(height: 20),


Creating the Game Board

We create a 3×3 grid for the game board using a GridView.builder. Each cell in the grid is a clickable GestureDetector.

Dart
SizedBox(
              height: 300,
              width: 300,
              child: GridView.builder(
                primary: true,
                padding: const EdgeInsets.all(6),
                itemCount: 9,
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3),
                itemBuilder: (context, index) {
                  return GestureDetector(
                    onTap: () {
                      if (moves[index] == "-") {
                        setState(() {
                          moves[index] = currentMove;
                          currentMove = currentMove == "O" ? "X" : "O";
                          count++;
                          if (_checkWinner()) {
                            _showDialog(context, "Winner!", "The winner is ${moves[index]}");
                          } else if (count == 9) {
                            _showDialog(context, "Draw!", "Match is DRAW");
                          }
                        });
                      }
                    },
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(color: Colors.black),
                        color: moves[index] == "-"
                            ? Colors.white
                            : (moves[index] == "X"
                                ? Colors.blue.shade100
                                : Colors.red.shade100),
                      ),
                      child: Center(
                        child: Text(
                          moves[index],
                          style: const TextStyle(
                            color: Colors.black,
                            fontSize: 40,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),
            const SizedBox(height: 20),


Adding the Reset Button

Finally, we add a button to restart the game.

Dart
  ElevatedButton(
              onPressed: _resetGame,
              child: const Text("Restart Game"),
            ),
          ],
        ),
      ),
    );
  }
}


Putting it all together, the complete code for the Tic Tac Toe page looks like this:

Dart
class TicTacToePage extends StatefulWidget {
  const TicTacToePage({super.key});
  @override
  State<TicTacToePage> createState() => _TicTacToePageState();
}
class _TicTacToePageState extends State<TicTacToePage> {
  List<String> moves = List.filled(9, "-");
  String currentMove = "X";
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Tic Tac Toe'),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "Current Move: $currentMove",
              style: const TextStyle(fontSize: 25),
            ),
            const SizedBox(height: 20),
            SizedBox(
              height: 300,
              width: 300,
              child: GridView.builder(
                primary: true,
                padding: const EdgeInsets.all(6),
                itemCount: 9,
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3),
                itemBuilder: (context, index) {
                  return GestureDetector(
                    onTap: () {
                      if (moves[index] == "-") {
                        setState(() {
                          moves[index] = currentMove;
                          currentMove = currentMove == "O" ? "X" : "O";
                          count++;
                          if (_checkWinner()) {
                            _showDialog(context, "Winner!", "The winner is ${moves[index]}");
                          } else if (count == 9) {
                            _showDialog(context, "Draw!", "Match is DRAW");
                          }
                        });
                      }
                    },
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(color: Colors.black),
                        color: moves[index] == "-"
                            ? Colors.white
                            : (moves[index] == "X"
                                ? Colors.blue.shade100
                                : Colors.red.shade100),
                      ),
                      child: Center(
                        child: Text(
                          moves[index],
                          style: const TextStyle(
                            color: Colors.black,
                            fontSize: 40,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _resetGame,
              child: const Text("Restart Game"),
            ),
          ],
        ),
      ),
    );
  }
}


Step 4: Add Game Logic

Resetting the Game

First, we’ll add a method to reset the game. This method will reinitialize the game board, set the current move to “X”, and reset the move count.

Dart
void _resetGame() {
  setState(() {
    moves = List.filled(9, "-");
    currentMove = "X";
    count = 0;
  });
}


Checking for a Winner

Next, we’ll add a method to check if there’s a winner. This method will go through all possible winning combinations and check if any of them have been achieved.

Dart
bool _checkWinner() {
  List<List<int>> winningCombinations = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6]
  ];
  for (var combo in winningCombinations) {
    if (moves[combo[0]] != "-" &&
        moves[combo[0]] == moves[combo[1]] &&
        moves[combo[1]] == moves[combo[2]]) {
      return true;
    }
  }
  return false;
}


Showing a Dialog

Lastly, we’ll add a method to show a dialog when the game ends. This method will display a dialog with the game’s result (either a win or a draw) and provide an option to restart the game.

Dart
void _showDialog(BuildContext context, String title, String content) {
  showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: Text(title),
        content: Text(content),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.of(context).pop();
              _resetGame();
            },
            child: const Text("Play Again"),
          ),
        ],
      );
    },
  );
}


Putting It All Together

Add the above methods to the _TicTacToePageState class. Here’s the complete code for the state class with all the methods included:

Dart
class _TicTacToePageState extends State<TicTacToePage> {
  List<String> moves = List.filled(9, "-");
  String currentMove = "X";
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Tic Tac Toe'),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "Current Move: $currentMove",
              style: const TextStyle(fontSize: 25),
            ),
            const SizedBox(height: 20),
            SizedBox(
              height: 300,
              width: 300,
              child: GridView.builder(
                primary: true,
                padding: const EdgeInsets.all(6),
                itemCount: 9,
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3),
                itemBuilder: (context, index) {
                  return GestureDetector(
                    onTap: () {
                      if (moves[index] == "-") {
                        setState(() {
                          moves[index] = currentMove;
                          currentMove = currentMove == "O" ? "X" : "O";
                          count++;
                          if (_checkWinner()) {
                            _showDialog(context, "Winner!", "The winner is ${moves[index]}");
                          } else if (count == 9) {
                            _showDialog(context, "Draw!", "Match is DRAW");
                          }
                        });
                      }
                    },
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(color: Colors.black),
                        color: moves[index] == "-"
                            ? Colors.white
                            : (moves[index] == "X"
                                ? Colors.blue.shade100
                                : Colors.red.shade100),
                      ),
                      child: Center(
                        child: Text(
                          moves[index],
                          style: const TextStyle(
                            color: Colors.black,
                            fontSize: 40,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _resetGame,
              child: const Text("Restart Game"),
            ),
          ],
        ),
      ),
    );
  }

  void _resetGame() {
    setState(() {
      moves = List.filled(9, "-");
      currentMove = "X";
      count = 0;
    });
  }

  bool _checkWinner() {
    List<List<int>> winningCombinations = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
    ];

    for (var combo in winningCombinations) {
      if (moves[combo[0]] != "-" &&
          moves[combo[0]] == moves[combo[1]] &&
          moves[combo[1]] == moves[combo[2]]) {
        return true;
      }
    }
    return false;
  }

  void _showDialog(BuildContext context, String title, String content) {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text(title),
          content: Text(content),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
                _resetGame();
              },
              child: const Text("Play Again"),
            ),
          ],
        );
      },
    );
  }
}


Complete code

main.dart
import 'package:flutter/material.dart';

// Running the Application
void main() {
  runApp(const MyApp());
}

// Stateless widget representing the main application
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Tic Tac Toe', // Application title
      debugShowCheckedModeBanner: false, // Disable debug banner
      home: TicTacToePage(), // Set TicTacToePage as the home page
    );
  }
}

// Stateful widget representing the Tic Tac Toe game page
class TicTacToePage extends StatefulWidget {
  const TicTacToePage({super.key});

  @override
  State<TicTacToePage> createState() => _TicTacToePageState();
}

// State class for the Tic Tac Toe game logic and UI
class _TicTacToePageState extends State<TicTacToePage> {
  // List to store the moves on the board
  List<String> moves = List.filled(9, "-");
  // Variable to track the current move
  String currentMove = "X";
  // Variable to count the number of moves made
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Tic Tac Toe'), // AppBar title
        centerTitle: true, // Center the title
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "Current Move: $currentMove", // Display the current move
              style: const TextStyle(fontSize: 25),
            ),
            const SizedBox(height: 20), // Spacer
            SizedBox(
              height: 300,
              width: 300,
              child: GridView.builder(
                primary: true,
                padding: const EdgeInsets.all(6),
                itemCount: 9, // Number of items in the grid
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3), // 3x3 grid
                itemBuilder: (context, index) {
                  return GestureDetector(
                    onTap: () {
                      if (moves[index] == "-") {
                        setState(() {
                          // Update the move
                          moves[index] = currentMove;
                          // Switch to the next move
                          currentMove = currentMove == "O" ? "X" : "O";
                          // Increment the move count
                          count++;
                          // Check for a winner or draw
                          if (_checkWinner()) {
                            _showDialog(context, "Winner!", "The winner is ${moves[index]}");
                          } else if (count == 9) {
                            _showDialog(context, "Draw!", "Match is DRAW");
                          }
                        });
                      }
                    },
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(color: Colors.black), // Cell border
                        color: moves[index] == "-"
                            ? Colors.white // Empty cell color
                            : (moves[index] == "X"
                                ? Colors.blue.shade100 // X cell color
                                : Colors.red.shade100), // O cell color
                      ),
                      child: Center(
                        child: Text(
                          moves[index], // Display the move
                          style: const TextStyle(
                            color: Colors.black,
                            fontSize: 40,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),
            const SizedBox(height: 20), // Spacer
            ElevatedButton(
              onPressed: _resetGame, // Restart game button
              child: const Text("Restart Game"),
            ),
          ],
        ),
      ),
    );
  }

  // Method to reset the game state
  void _resetGame() {
    setState(() {
      moves = List.filled(9, "-"); // Reset moves
      currentMove = "X"; // Reset to initial move
      count = 0; // Reset move count
    });
  }

  // Method to check for a winning combination
  bool _checkWinner() {
    List<List<int>> winningCombinations = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
    ];

    for (var combo in winningCombinations) {
      if (moves[combo[0]] != "-" &&
          moves[combo[0]] == moves[combo[1]] &&
          moves[combo[1]] == moves[combo[2]]) {
        return true;
      }
    }
    return false;
  }

  // Method to display a dialog with the game result
  void _showDialog(BuildContext context, String title, String content) {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text(title), // Dialog title
          content: Text(content), // Dialog content
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop(); // Close the dialog
                _resetGame(); // Reset the game
              },
              child: const Text("Play Again"),
            ),
          ],
        );
      },
    );
  }
}


Step 5: Testing Your Game

Now, you can run your app on an emulator or a physical device. Use the following command to run your Flutter app:

flutter run

You’ll see your Tic Tac Toe game in action! Click on the cells to make moves and see who wins or if it’s a draw.

Output:


Note : To access the full android application check this repository: Simple Tic Tac Toe Game in Flutter




Reffered: https://www.geeksforgeeks.org


Android

Related
Base Line Constraints in Android Base Line Constraints in Android
Constraints in Android Constraints in Android
Master Android Development With Kotlin: A Complete Guide Master Android Development With Kotlin: A Complete Guide
Android Java Projects - Build Your First Android App from Scratch Android Java Projects - Build Your First Android App from Scratch
How to Build a Simple Music Player App using Android Kotlin How to Build a Simple Music Player App using Android Kotlin

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
20