Flutter - Building a Tic Tac Toe Game - GeeksforGeeks

29 Oct, 2021
Flutter SDK is an open-source software development kit for building beautiful UI which is natively compiled. In this article, we will build a Tic Tac Toe Game. We will be using VS Code IDE for this project, you can also use Android Studio. Concepts covered are:
Showing Widgets on the screen.
GridView.builder
Function writing
GestureDetector
If and else in dart
Follow the below steps to implement the Tic-Tac-Toe game. Let’s get started.
Open the Terminal /Command-prompt. Change Directory to your choice and run 
flutter create tic_tac_toe
Open the tic-tac-toe in VS Code or Android Studio.
In the Lib folder, there is a main.dart file already present. Now, add the following code in main.dart file.
UI for Tic-Tac-Toe
Dart
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return MaterialApp(      home: HomePage(),    );  }} class HomePage extends StatefulWidget {  @override  _HomePageState createState() => _HomePageState();} class _HomePageState extends State<HomePage> {     // declarations  bool oTurn = true;     // 1st player is O  List<String> displayElement = ['', '', '', '', '', '', '', '', ''];  int oScore = 0;  int xScore = 0;  int filledBoxes = 0;   @override  Widget build(BuildContext context) {    return Scaffold(      backgroundColor: Colors.indigo[900],      body: Column(        children: <Widget>[          Expanded(                         // creating the ScoreBoard            child: Container(              child: Row(                mainAxisAlignment: MainAxisAlignment.center,                children: <Widget>[                  Padding(                    padding: const EdgeInsets.all(30.0),                    child: Column(                      mainAxisAlignment: MainAxisAlignment.center,                      children: <Widget>[                        Text(                          'Player X',                          style: TextStyle(fontSize: 20,                                           fontWeight: FontWeight.bold,                                           color: Colors.white),                        ),                        Text(                          xScore.toString(),                          style: TextStyle(fontSize: 20,color: Colors.white),                        ),                      ],                    ),                  ),                  Padding(                    padding: const EdgeInsets.all(30.0),                    child: Column(                      mainAxisAlignment: MainAxisAlignment.center,                      children: <Widget>[                        Text('Player O', style: TextStyle(fontSize: 20,                                                          fontWeight: FontWeight.bold,                                                          color: Colors.white)                        ),                        Text(                          oScore.toString(),                          style: TextStyle(fontSize: 20,color: Colors.white),                        ),                      ],                    ),                  ),                ],              ),            ),          ),          Expanded(                         // Creating the Board for Tic tac toe            flex: 4,            child: GridView.builder(                itemCount: 9,                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(                    crossAxisCount: 3),                itemBuilder: (BuildContext context, int index) {                  return GestureDetector(                    onTap: () {                      _tapped(index);                    },                    child: Container(                      decoration: BoxDecoration(                          border: Border.all(color: Colors.white)),                      child: Center(                        child: Text(                          displayElement[index],                          style: TextStyle(color: Colors.white, fontSize: 35),                        ),                      ),                    ),                  );                }),          ),          Expanded(                         // Button for Clearing the Enter board            // as well as Scoreboard to start allover again            child: Container(            child: Row(              mainAxisAlignment: MainAxisAlignment.center,              children: <Widget>[                RaisedButton(                  color: Colors.indigo[50],                  textColor: Colors.black,                  onPressed: _clearScoreBoard,                  child: Text("Clear Score Board"),                ),              ],            ),          ))        ],      ),    );  }   // filling the boxes when tapped with X  // or O respectively and then checking the winner function  void _tapped(int index) {    setState(() {      if (oTurn && displayElement[index] == '') {        displayElement[index] = 'O';        filledBoxes++;      } else if (!oTurn && displayElement[index] == '') {        displayElement[index] = 'X';        filledBoxes++;      }       oTurn = !oTurn;      _checkWinner();    });  }   }
Function for Checking the Winner: The below-defined function can be used to check for the winner in the game. It uses the same logic as the game itself, ie, based on the elements in each cell.
Dart
void _checkWinner() {       // Checking rows    if (displayElement[0] == displayElement[1] &&        displayElement[0] == displayElement[2] &&        displayElement[0] != '') {      _showWinDialog(displayElement[0]);    }    if (displayElement[3] == displayElement[4] &&        displayElement[3] == displayElement[5] &&        displayElement[3] != '') {      _showWinDialog(displayElement[3]);    }    if (displayElement[6] == displayElement[7] &&        displayElement[6] == displayElement[8] &&        displayElement[6] != '') {      _showWinDialog(displayElement[6]);    }     // Checking Column    if (displayElement[0] == displayElement[3] &&        displayElement[0] == displayElement[6] &&        displayElement[0] != '') {      _showWinDialog(displayElement[0]);    }    if (displayElement[1] == displayElement[4] &&        displayElement[1] == displayElement[7] &&        displayElement[1] != '') {      _showWinDialog(displayElement[1]);    }    if (displayElement[2] == displayElement[5] &&        displayElement[2] == displayElement[8] &&        displayElement[2] != '') {      _showWinDialog(displayElement[2]);    }     // Checking Diagonal    if (displayElement[0] == displayElement[4] &&        displayElement[0] == displayElement[8] &&        displayElement[0] != '') {      _showWinDialog(displayElement[0]);    }    if (displayElement[2] == displayElement[4] &&        displayElement[2] == displayElement[6] &&        displayElement[2] != '') {      _showWinDialog(displayElement[2]);    } else if (filledBoxes == 9) {      _showDrawDialog();    }  }
Code for Win & Draw Dialog Boxes: The below codes shows the dialog box whenever a user wins or the match is a draw.
Dart
void _showWinDialog(String winner) {    showDialog(        barrierDismissible: false,        context: context,        builder: (BuildContext context) {          return AlertDialog(            title: Text("\" " + winner + " \" is Winner!!!"),            actions: [              FlatButton(                child: Text("Play Again"),                onPressed: () {                  _clearBoard();                  Navigator.of(context).pop();                },              )            ],          );        });     if (winner == 'O') {      oScore++;    } else if (winner == 'X') {      xScore++;    }  }   void _showDrawDialog() {    showDialog(        barrierDismissible: false,        context: context,        builder: (BuildContext context) {          return AlertDialog(            title: Text("Draw"),            actions: [              FlatButton(                child: Text("Play Again"),                onPressed: () {                  _clearBoard();                  Navigator.of(context).pop();                },              )            ],          );        });  }
Clearing the Boards after the Game-over: The below code can be used to clear the board once the game is over. 
Dart
void _clearBoard() {   setState(() {     for (int i = 0; i < 9; i++) {       displayElement[i] = '';     }   });    filledBoxes = 0; }  void _clearScoreBoard() {   setState(() {     xScore = 0;     oScore = 0;     for (int i = 0; i < 9; i++) {       displayElement[i] = '';     }   });   filledBoxes = 0; }
Complete Source Code: 
Dart
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return MaterialApp(      home: HomePage(),    );  }} class HomePage extends StatefulWidget {  @override  _HomePageState createState() => _HomePageState();} class _HomePageState extends State<HomePage> {  bool oTurn = true;     // 1st player is O  List<String> displayElement = ['', '', '', '', '', '', '', '', ''];  int oScore = 0;  int xScore = 0;  int filledBoxes = 0;   @override  Widget build(BuildContext context) {    return Scaffold(      backgroundColor: Colors.indigo[900],      body: Column(        children: <Widget>[          Expanded(            child: Container(              child: Row(                mainAxisAlignment: MainAxisAlignment.center,                children: <Widget>[                  Padding(                    padding: const EdgeInsets.all(30.0),                    child: Column(                      mainAxisAlignment: MainAxisAlignment.center,                      children: <Widget>[                        Text(                          'Player X',                          style: TextStyle(fontSize: 20,                                           fontWeight: FontWeight.bold,                                           color: Colors.white),                        ),                        Text(                          xScore.toString(),                          style: TextStyle(fontSize: 20,color: Colors.white),                        ),                      ],                    ),                  ),                  Padding(                    padding: const EdgeInsets.all(30.0),                    child: Column(                      mainAxisAlignment: MainAxisAlignment.center,                      children: <Widget>[                        Text('Player O', style: TextStyle(fontSize: 20,                                                          fontWeight: FontWeight.bold,                                                          color: Colors.white)                        ),                        Text(                          oScore.toString(),                          style: TextStyle(fontSize: 20,color: Colors.white),                        ),                      ],                    ),                  ),                ],              ),            ),          ),          Expanded(            flex: 4,            child: GridView.builder(                itemCount: 9,                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(                    crossAxisCount: 3),                itemBuilder: (BuildContext context, int index) {                  return GestureDetector(                    onTap: () {                      _tapped(index);                    },                    child: Container(                      decoration: BoxDecoration(                          border: Border.all(color: Colors.white)),                      child: Center(                        child: Text(                          displayElement[index],                          style: TextStyle(color: Colors.white, fontSize: 35),                        ),                      ),                    ),                  );                }),          ),          Expanded(              child: Container(            child: Row(              mainAxisAlignment: MainAxisAlignment.center,              children: <Widget>[                RaisedButton(                  color: Colors.indigo[50],                  textColor: Colors.black,                  onPressed: _clearScoreBoard,                  child: Text("Clear Score Board"),                ),              ],            ),          ))        ],      ),    );  }   void _tapped(int index) {    setState(() {      if (oTurn && displayElement[index] == '') {        displayElement[index] = 'O';        filledBoxes++;      } else if (!oTurn && displayElement[index] == '') {        displayElement[index] = 'X';        filledBoxes++;      }       oTurn = !oTurn;      _checkWinner();    });  }   void _checkWinner() {         // Checking rows    if (displayElement[0] == displayElement[1] &&        displayElement[0] == displayElement[2] &&        displayElement[0] != '') {      _showWinDialog(displayElement[0]);    }    if (displayElement[3] == displayElement[4] &&        displayElement[3] == displayElement[5] &&        displayElement[3] != '') {      _showWinDialog(displayElement[3]);    }    if (displayElement[6] == displayElement[7] &&        displayElement[6] == displayElement[8] &&        displayElement[6] != '') {      _showWinDialog(displayElement[6]);    }     // Checking Column    if (displayElement[0] == displayElement[3] &&        displayElement[0] == displayElement[6] &&        displayElement[0] != '') {      _showWinDialog(displayElement[0]);    }    if (displayElement[1] == displayElement[4] &&        displayElement[1] == displayElement[7] &&        displayElement[1] != '') {      _showWinDialog(displayElement[1]);    }    if (displayElement[2] == displayElement[5] &&        displayElement[2] == displayElement[8] &&        displayElement[2] != '') {      _showWinDialog(displayElement[2]);    }     // Checking Diagonal    if (displayElement[0] == displayElement[4] &&        displayElement[0] == displayElement[8] &&        displayElement[0] != '') {      _showWinDialog(displayElement[0]);    }    if (displayElement[2] == displayElement[4] &&        displayElement[2] == displayElement[6] &&        displayElement[2] != '') {      _showWinDialog(displayElement[2]);    } else if (filledBoxes == 9) {      _showDrawDialog();    }  }   void _showWinDialog(String winner) {    showDialog(        barrierDismissible: false,        context: context,        builder: (BuildContext context) {          return AlertDialog(            title: Text("\" " + winner + " \" is Winner!!!"),            actions: [              FlatButton(                child: Text("Play Again"),                onPressed: () {                  _clearBoard();                  Navigator.of(context).pop();                },              )            ],          );        });     if (winner == 'O') {      oScore++;    } else if (winner == 'X') {      xScore++;    }  }   void _showDrawDialog() {    showDialog(        barrierDismissible: false,        context: context,        builder: (BuildContext context) {          return AlertDialog(            title: Text("Draw"),            actions: [              FlatButton(                child: Text("Play Again"),                onPressed: () {                  _clearBoard();                  Navigator.of(context).pop();                },              )            ],          );        });  }   void _clearBoard() {    setState(() {      for (int i = 0; i < 9; i++) {        displayElement[i] = '';      }    });     filledBoxes = 0;  }   void _clearScoreBoard() {    setState(() {      xScore = 0;      oScore = 0;      for (int i = 0; i < 9; i++) {        displayElement[i] = '';      }    });    filledBoxes = 0;  }}
In Flutter main.dart file is the entry point from which the code starts to execute. In the main.dart file firstly material design package has been imported. Then a function runApp has been created with parameter as MyApp. After the declaration of class MyApp which is a stateless widget, the state of class MyApp has been laid out. 
Output:
saurabh1990aror
adarshco077
Flutter
Android
Dart
Flutter
Android
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Resource Raw Folder in Android Studio
Flutter - Custom Bottom Navigation Bar
How to Read Data from SQLite Database in Android?
Retrofit with Kotlin Coroutine in Android
How to Post Data to API using Retrofit in Android?
Flutter - DropDownButton Widget
Listview.builder in Flutter
Flutter - Asset Image
Splash Screen in Flutter
Flutter - Custom Bottom Navigation Bar