Code is copied!
How To Create a Torch App Using Flutter
In this project, we're excited to present our "Tic-Tac-Toe Challenge" app, developed using Flutter. This classic game of strategy and skill is now at your fingertips, providing hours of competitive fun. The journey begins by inviting two players to the board. You'll start by entering their names, giving a personal touch to the game Once the players are set, they are assigned their respective symbols, "X" and "O." The game is all about outsmarting your opponent, and the first player to do so is declared the champion. The moment one of the players achieves a winning sequence, their name is triumphantly displayed, announcing their victory. However, in the spirit of fair play, if neither player secures a winning sequence and the grid is entirely filled, the game recognizes a draw, and the word "Draw" gracefully appears. It's a testament to the evenly matched battle that can take place in this iconic game. Our project will guide you through the creation of this engaging Tic-Tac-Toe app using Flutter. You'll learn how to build an intuitive and responsive user interface, manage player input, and provide real-time feedback to keep the game exciting. Whether you're a seasoned developer or new to the world of app development, this project offers a hands-on experience in building a fun and interactive game.
Source Code for the main.dart file
Add the following Code inside your main.dart file :
import 'package:flutter/material.dart';
import 'package:tictactoe/Screen.dart';
import 'package:google_fonts/google_fonts.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tic Tac Toe',
debugShowCheckedModeBanner: false,
home: Screen(),
);
}
}
import 'package:flutter/material.dart';
import 'package:tictactoe/Screen.dart';
import 'package:google_fonts/google_fonts.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tic Tac Toe',
debugShowCheckedModeBanner: false,
home: Screen(),
);
}
}
Source Code for the Screen.dart file
Add the following Code inside your Screen.dart file :
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:tictactoe/Game.dart';
class Screen extends StatefulWidget {
const Screen({super.key});
@override
State createState() => _ScreenState();
}
class _ScreenState extends State {
String? player1;
String? player2;
LinearGradient linear=LinearGradient(
colors: [Color(0xFF5A0DF3),Color(0xFF261765)],
stops: [0.0,1.0],
begin: Alignment.topCenter,
end: Alignment.bottomCenter
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient:linear
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
child:Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Enter Player Name',
style: GoogleFonts.roboto(
textStyle: TextStyle(
color: Color(0xFFE6C621),
fontWeight: FontWeight.w900,
fontSize: 30,
),
),
),
SizedBox(height: 30,),
HeadingText(name: 'Player 1'),
SizedBox(height: 3,),
SizedBox(height: 20,),
TextField(
onChanged: (value){
setState(() {
player1=value;
});
},
decoration: InputDecoration(
hintText: 'Enter',
filled: true,
fillColor: Colors.white,
focusColor: Color(0xFFE5B506),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFE5B506), // Border color when focused/tapped
width: 4.0, // Border width
),
),
hintStyle: TextStyle(
color: Colors.black45
),
border: OutlineInputBorder(),
),
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
fontWeight: FontWeight.bold
),
),
SizedBox(height: 30,),
HeadingText(name: 'Player 2'),
SizedBox(height: 30,),
TextField(
onChanged: (value){
setState(() {
player2=value;
});
},
decoration: InputDecoration(
hintText: 'Enter',
filled: true,
fillColor: Colors.white,
focusColor: Color(0xFFE5B506),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFE5B506), // Border color when focused/tapped
width: 4.0, // Border width
),
),
hintStyle: TextStyle(
color: Colors.black45
),
border: OutlineInputBorder(),
),
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
fontWeight: FontWeight.bold
),
),
SizedBox(height: 45,),
Container(
width: double.infinity,
height: 65,
child: ElevatedButton(
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder:
(context)=>Game(
player1:player1! ,
player2: player2!,
)));
},
child: Text(
'Start Game',
style: GoogleFonts.roboto(
textStyle: TextStyle(
fontSize: 30,
fontWeight: FontWeight.w900
)
)
),
style: ElevatedButton.styleFrom(
elevation: 5.0,
shape: RoundedRectangleBorder(borderRadius:
BorderRadius.circular(50)),
backgroundColor: Color(0xFFE6C621),
foregroundColor:Color(0xFF2F1E60)
),
),
)
]
),
),
) ,
),
),
);
}
}
class HeadingText extends StatelessWidget {
const HeadingText({required this.name});
final String name;
@override
Widget build(BuildContext context) {
return Text(
name,
style:GoogleFonts.openSans(
textStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 25
)
)
);
}
}
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:tictactoe/Game.dart';
class Screen extends StatefulWidget {
const Screen({super.key});
@override
State createState() => _ScreenState();
}
class _ScreenState extends State {
String? player1;
String? player2;
LinearGradient linear=LinearGradient(
colors: [Color(0xFF5A0DF3),Color(0xFF261765)],
stops: [0.0,1.0],
begin: Alignment.topCenter,
end: Alignment.bottomCenter
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient:linear
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
child:Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Enter Player Name',
style: GoogleFonts.roboto(
textStyle: TextStyle(
color: Color(0xFFE6C621),
fontWeight: FontWeight.w900,
fontSize: 30,
),
),
),
SizedBox(height: 30,),
HeadingText(name: 'Player 1'),
SizedBox(height: 3,),
SizedBox(height: 20,),
TextField(
onChanged: (value){
setState(() {
player1=value;
});
},
decoration: InputDecoration(
hintText: 'Enter',
filled: true,
fillColor: Colors.white,
focusColor: Color(0xFFE5B506),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFE5B506), // Border color when focused/tapped
width: 4.0, // Border width
),
),
hintStyle: TextStyle(
color: Colors.black45
),
border: OutlineInputBorder(),
),
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
fontWeight: FontWeight.bold
),
),
SizedBox(height: 30,),
HeadingText(name: 'Player 2'),
SizedBox(height: 30,),
TextField(
onChanged: (value){
setState(() {
player2=value;
});
},
decoration: InputDecoration(
hintText: 'Enter',
filled: true,
fillColor: Colors.white,
focusColor: Color(0xFFE5B506),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFE5B506), // Border color when focused/tapped
width: 4.0, // Border width
),
),
hintStyle: TextStyle(
color: Colors.black45
),
border: OutlineInputBorder(),
),
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
fontWeight: FontWeight.bold
),
),
SizedBox(height: 45,),
Container(
width: double.infinity,
height: 65,
child: ElevatedButton(
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder:
(context)=>Game(
player1:player1! ,
player2: player2!,
)));
},
child: Text(
'Start Game',
style: GoogleFonts.roboto(
textStyle: TextStyle(
fontSize: 30,
fontWeight: FontWeight.w900
)
)
),
style: ElevatedButton.styleFrom(
elevation: 5.0,
shape: RoundedRectangleBorder(borderRadius:
BorderRadius.circular(50)),
backgroundColor: Color(0xFFE6C621),
foregroundColor:Color(0xFF2F1E60)
),
),
)
]
),
),
) ,
),
),
);
}
}
class HeadingText extends StatelessWidget {
const HeadingText({required this.name});
final String name;
@override
Widget build(BuildContext context) {
return Text(
name,
style:GoogleFonts.openSans(
textStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 25
)
)
);
}
}
Source Code for the Game.dart file
Add the following Code inside your Game.dart file :
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class Game extends StatefulWidget {
Game({required this.player1,required this.player2});
final String player1;
final String player2;
@override
State createState() => _GameState();
}
class _GameState extends State {
final String player1val="X";
final String player2val="O";
String? currentPlayer;
String? currentPlayerval;
bool? gameOver;
List? board;
LinearGradient linear=LinearGradient(
colors: [Color(0xFF5A0DF3),Color(0xFF261765)],
stops: [0.0,1.0],
begin: Alignment.topCenter,
end: Alignment.bottomCenter
);
@override
void initState() {
// TODO: implement initState
super.initState();
intializeGame();
}
void changeturn(){
if(currentPlayerval==player1val){
currentPlayerval=player2val;
currentPlayer='${widget.player2}(O)';
}
else{
currentPlayerval=player1val;
currentPlayer='${widget.player1}(X)';
}
}
void winningcheck(){
List> winningpattern=[
[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 winningpatternposition in winningpattern)
{
String winningpatternposition0=board![winningpatternposition[0]];
String winningpatternposition1=board![winningpatternposition[1]];
String winningpatternposition2=board![winningpatternposition[2]];
if(winningpatternposition1.isNotEmpty){
if(winningpatternposition0==winningpatternposition1 &&
winningpatternposition1==winningpatternposition2){
// all equals
if(winningpatternposition1=="X") {
GameOverMessage("${widget.player1} Wins");
gameOver = true;
return;
}
else{
GameOverMessage("${widget.player2} Wins");
gameOver = true;
return;
}
}
}
}
}
void checkfordraw(){
if(gameOver!){
return;
}
bool check=true;
for(var boardfilled in board!)
{
if(boardfilled.isEmpty){
check=false;
}
}
if(check){
GameOverMessage("Draw");
gameOver=true;
}
}
GameOverMessage(String message) {
showDialog(
context: context,
builder: (BuildContext context) {
return Center(
child: AlertDialog(
backgroundColor: Color(0xFF5A0DF3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0), // Set border radius here
),
actions: [
Center(
child: Text("Game Over",style: TextStyle(
fontSize: 40,
color: Color(0xFFE6C621),
fontWeight: FontWeight.w900
),),
),
SizedBox(height: 30,),
Center(
child: Text("$message", style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.w900,
color: Colors.white
),),
),
SizedBox(height: 40,),
Center(
child: Container(
width: 200.0,
height: 55.0,
child: ElevatedButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Icon(Icons.restart_alt,
size: 40.0,
color: Colors.white,),
Text('Restart', style: TextStyle(
fontSize: 27.0, fontWeight: FontWeight.bold,color: Colors.white),),
],
),
style: ElevatedButton.styleFrom(
elevation: 5,
backgroundColor: Color(0xFFE6C621),
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5)
)
),
onPressed: () {
Navigator.of(context).pop();
setState(() {
intializeGame();
});
},
),
),
),
SizedBox(height: 40,)
],
),
);
}
);
}
void intializeGame(){
currentPlayer='${widget.player1}(X)';
currentPlayerval=player1val;
gameOver=false;
board=["","","","","","","","",""];
}
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient: linear
),
child: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 95,),
Column(
children:[
Text(
'Turn:',
style: GoogleFonts.roboto(
textStyle: TextStyle(
color: Color(0xFFE6C621),
fontWeight: FontWeight.w900,
fontSize: 60,
),
)
),
Text(
currentPlayer!,
style: GoogleFonts.roboto(
textStyle: TextStyle(
color: Color(0xFFE6C621),
fontWeight: FontWeight.w900,
fontSize: 45,
),
)
),
]
),
SizedBox(height: 30,),
Container(
width: MediaQuery.of(context).size.height / 2,
height: MediaQuery.of(context).size.height / 2,
margin:EdgeInsets.all(10),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3
),
itemCount: board!.length,
itemBuilder: (context,index){
return GestureDetector(
onTap: (){
if (gameOver! || board![index].isNotEmpty){
return;
}
setState(() {
board![index]=currentPlayerval!;
changeturn();
winningcheck();
checkfordraw();
});
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color:Color(0xFF221355),
),
margin: EdgeInsets.all(10),
child: Center(
child: Text(
board![index],
style:GoogleFonts.roboto(
textStyle: TextStyle(
color: Color(0xFFE6C621),
fontSize: 65,
fontWeight: FontWeight.bold,
)
)
),
),
),
);
},
),
),
]
)
),
),
),
);
}}
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class Game extends StatefulWidget {
Game({required this.player1,required this.player2});
final String player1;
final String player2;
@override
State createState() => _GameState();
}
class _GameState extends State {
final String player1val="X";
final String player2val="O";
String? currentPlayer;
String? currentPlayerval;
bool? gameOver;
List? board;
LinearGradient linear=LinearGradient(
colors: [Color(0xFF5A0DF3),Color(0xFF261765)],
stops: [0.0,1.0],
begin: Alignment.topCenter,
end: Alignment.bottomCenter
);
@override
void initState() {
// TODO: implement initState
super.initState();
intializeGame();
}
void changeturn(){
if(currentPlayerval==player1val){
currentPlayerval=player2val;
currentPlayer='${widget.player2}(O)';
}
else{
currentPlayerval=player1val;
currentPlayer='${widget.player1}(X)';
}
}
void winningcheck(){
List> winningpattern=[
[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 winningpatternposition in winningpattern)
{
String winningpatternposition0=board![winningpatternposition[0]];
String winningpatternposition1=board![winningpatternposition[1]];
String winningpatternposition2=board![winningpatternposition[2]];
if(winningpatternposition1.isNotEmpty){
if(winningpatternposition0==winningpatternposition1 &&
winningpatternposition1==winningpatternposition2){
// all equals
if(winningpatternposition1=="X") {
GameOverMessage("${widget.player1} Wins");
gameOver = true;
return;
}
else{
GameOverMessage("${widget.player2} Wins");
gameOver = true;
return;
}
}
}
}
}
void checkfordraw(){
if(gameOver!){
return;
}
bool check=true;
for(var boardfilled in board!)
{
if(boardfilled.isEmpty){
check=false;
}
}
if(check){
GameOverMessage("Draw");
gameOver=true;
}
}
GameOverMessage(String message) {
showDialog(
context: context,
builder: (BuildContext context) {
return Center(
child: AlertDialog(
backgroundColor: Color(0xFF5A0DF3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0), // Set border radius here
),
actions: [
Center(
child: Text("Game Over",style: TextStyle(
fontSize: 40,
color: Color(0xFFE6C621),
fontWeight: FontWeight.w900
),),
),
SizedBox(height: 30,),
Center(
child: Text("$message", style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.w900,
color: Colors.white
),),
),
SizedBox(height: 40,),
Center(
child: Container(
width: 200.0,
height: 55.0,
child: ElevatedButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Icon(Icons.restart_alt,
size: 40.0,
color: Colors.white,),
Text('Restart', style: TextStyle(
fontSize: 27.0, fontWeight: FontWeight.bold,color: Colors.white),),
],
),
style: ElevatedButton.styleFrom(
elevation: 5,
backgroundColor: Color(0xFFE6C621),
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5)
)
),
onPressed: () {
Navigator.of(context).pop();
setState(() {
intializeGame();
});
},
),
),
),
SizedBox(height: 40,)
],
),
);
}
);
}
void intializeGame(){
currentPlayer='${widget.player1}(X)';
currentPlayerval=player1val;
gameOver=false;
board=["","","","","","","","",""];
}
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient: linear
),
child: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 95,),
Column(
children:[
Text(
'Turn:',
style: GoogleFonts.roboto(
textStyle: TextStyle(
color: Color(0xFFE6C621),
fontWeight: FontWeight.w900,
fontSize: 60,
),
)
),
Text(
currentPlayer!,
style: GoogleFonts.roboto(
textStyle: TextStyle(
color: Color(0xFFE6C621),
fontWeight: FontWeight.w900,
fontSize: 45,
),
)
),
]
),
SizedBox(height: 30,),
Container(
width: MediaQuery.of(context).size.height / 2,
height: MediaQuery.of(context).size.height / 2,
margin:EdgeInsets.all(10),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3
),
itemCount: board!.length,
itemBuilder: (context,index){
return GestureDetector(
onTap: (){
if (gameOver! || board![index].isNotEmpty){
return;
}
setState(() {
board![index]=currentPlayerval!;
changeturn();
winningcheck();
checkfordraw();
});
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color:Color(0xFF221355),
),
margin: EdgeInsets.all(10),
child: Center(
child: Text(
board![index],
style:GoogleFonts.roboto(
textStyle: TextStyle(
color: Color(0xFFE6C621),
fontSize: 65,
fontWeight: FontWeight.bold,
)
)
),
),
),
);
},
),
),
]
)
),
),
),
);
}}