Tutorial On How To Build A Tic-tac-toe App Using Dart
CREATING A FLUTTER APP
INTRODUCTION
The Tic Tac Toe app is built with the famous ‘x’ and ‘o’ game in mind. In this article, with the help of geeksforgeeks.com, we will build a Tic Tac Toe Game using Flutter - Dart**.**
Flutter SDK is an open-source, natively built software development kit for creating stunning user interfaces. The VS Code IDE can be used, and Android Studio is also an option.
The concepts covered are:
Showing Widgets on the screen.
GridView.builder
Function writing
GestureDetector
If and else in dart
SOME STEPS TO CODING TIC-TAC-TOE APP
STEP 1: THE CLASS THAT RUNS THE APPLICATION
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;
The code in the main() function creates a new instance of the MyApp class.
This class is used to run the application.
The build() function returns a MaterialApp object.
The HomePage class extends StatelessWidget, so this object will not have any state associated with it.
However, the _HomePageState class is defined and will have state associated with it.
When the _HomePageState class is created, its createState() method is called.
This method sets up some basic initializations for the HomePage object and then returns it as a StatefulWidget instance.
The createState() method in the HomePage class overrides the default implementation in StatelessWidget .
This means that when the HomePage object is created, its own state will be set up instead of using whatever state was provided by the MyApp instance that created it.
The code in main() calls build() on the MyApp object to return a MaterialApp object containing an instance of HomePage .
STEP 2: PLAYER 'O' AND PLAYER 'X'
// 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();
});
}
The code creates a new Flutter application.
The HomePage class is used to represent the main page of the application.
This class extends StatelessWidget which means that it doesn’t maintain any state, and instead relies on the MaterialApp widget to provide all of its functionality.
Next, the build method is called.
This method is used to configure and create the MaterialApp widget.
The home property of this widget will be set to the HomePage class instance, and all other properties will be passed in as arguments.
Finally, the _HomePageState object is created and initialized with the default state for HomePage.
The code starts by creating a few variables to track the state of the game.
The oTurn variable tracks whether or not the player is currently making their turn, and displayElement stores an index into an array of Widget objects that will be used to display information about the game.
Next, the code sets up two buttons: one to clear the current score board and another to show a win dialog box if someone has already won.
The _showWinDialog function calls different methods depending on which button was tapped.
If it was tapped on the clear button, it calls clearScoreBoard; if it was tapped on the win dialog box, it calls showWinDialog with the appropriate widget object as its argument.
STEP 3: CHECK WINNER
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();
}
}
The next section of code checks each row in turn and displays information about who has won in that row.
First, it checks to see if any of the rows contain a widget object with the value “O” for player 1 or “X” for player 2.
If so, then that row is marked as having been won by either player and displayed accordingly.
Next, it checks each column in turn and does a similar check for widgets with values corresponding to those displayed in those columns (in this case
The code has an _tapped() function that is called when the user taps a row or column.
The _tapped() function checks to see if the tapped row or column corresponds to a winning row or column.
If it does, the _showWinDialog() function is called to display the win dialog box.
Otherwise, nothing happens.
The _checkWinner() function is called after every tap to check for a winning row or column.
If there is a winning row or column, the appropriate buttons are displayed in the win dialog box and the game is over.
The code first checks to see if the user has selected a winner in the game.
If they have, the code displays an alert dialog box with the winning message.
If not, it displays a normal dialog box.
The first line of code checks to see if the user has selected a winner by comparing two strings.
The first string is the display element for the player who has won (in this case, “winner”).
The second string is the display element for the player who has lost (in this case, “loser”).
If they are equal, then it means that the user has selected a winner and so we can stop checking for winners and just show the alert dialog box.
Next, we create an instance of our BuildContext class and pass it into our AlertDialog constructor.
This will allow us to customize some of its properties before displaying the dialog box.
We set barrierDismissible to false so that we can dismiss it automatically when users click on OK or Cancel.
We also set context to be our current context object so that we can access all of its properties and methods.
Finally, we set builder to be an anonymous function which will return our custom AlertDialog instance.)).
The code first checks if the displayed element is a winner.
If it is, the code then calls the showDialog() method which displays an alert dialog box with the text “The winner is:”.
If the displayed element isn’t a winner, then the code checks to see if there are any diagonal filled boxes.
If there are, then it calls the showDrawDialog() method which displays a draw dialog box.
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;
}
}
REFERENCES
Credit goes to:
https://www.geeksforgeeks.org/flutter-building-a-tic-tac-toe-game/