1

Set up the Main Function and Snake Game Widget

First, create a simple app entry point with the main() function and define the SnakeGame widget, which will hold the entire game.

πŸ’‘ Tip: Use the button on the right side to easily copy the code.

import 'package:flutter/material.dart';

void main() {
    runApp(MaterialApp(home: SnakeGame()));
}

class SnakeGame extends StatefulWidget {
    @override
    _SnakeGameState createState() => _SnakeGameState();
}
2

Initialize Game Variables and State

Inside _SnakeGameState, define essential game variables such as snake, food, direction, score, and the grid size. Also, initialize resetGame() to set up the initial state of the game.

πŸ’‘ Tip: Use the button on the right side to easily copy the code.

class _SnakeGameState extends State<SnakeGame> {
final int gridSize = 20; // Define grid size
List<Point> snake = []; // Holds snake segments
Point food = Point(0, 0); // Position of food
Direction direction = Direction.right; // Initial snake direction
int score = 0; // Player's score
bool isGameRunning = false; // Game state flag

@override
void initState() {
    super.initState();
    resetGame(); // Reset game on start
}

void resetGame() {
    snake = [
    Point(gridSize ~/ 2, gridSize ~/ 2),
    Point(gridSize ~/ 2 - 1, gridSize ~/ 2),
    Point(gridSize ~/ 2 - 2, gridSize ~/ 2),
    ];
    generateFood();
    direction = Direction.right;
    score = 0;
}
}
3

Generate Food

Define generateFood() to place food randomly on the grid where there are no snake segments.

πŸ’‘ Tip: Use the button on the right side to easily copy the code.

void generateFood() {
    final random = Random();
    do {
        food = Point(random.nextInt(gridSize), random.nextInt(gridSize));
    } while (snake.contains(food)); // Ensure food is not on the snake
    }
                              
4

Start and End Game Logic

Add startGame() to initiate the game and endGame() to end it, including a dialog box to show the final score.

πŸ’‘ Tip: Use the button on the right side to easily copy the code.

void startGame() {
    if (!isGameRunning) {
        setState(() {
        isGameRunning = true;
        resetGame();
        });
        Timer.periodic(Duration(milliseconds: 200), (timer) {
        if (isGameRunning) updateGame();
        });
    }
    }
    
    void endGame() {
    setState(() {
        isGameRunning = false;
    });
    showDialog(
        context: context,
        barrierDismissible: false,
        builder: (context) => AlertDialog(
        title: Text('Game Over'),
        content: Text('Final Score: $score'),
        actions: [
            TextButton(
            onPressed: () {
                Navigator.of(context).pop();
                resetGame();
            },
            child: Text('Play Again'),
            ),
        ],
        ),
    );
    }
5

Update Game State and Handle Snake Growth

Implement updateGame() to manage the snake’s movement, growth, and collisions with walls, itself, and food.

πŸ’‘ Tip: Use the button on the right side to easily copy the code.

void updateGame() {
final head = snake.first;
Point newHead;

switch (direction) {
    case Direction.right:
    newHead = Point(head.x + 1, head.y);
    break;
    case Direction.left:
    newHead = Point(head.x - 1, head.y);
    break;
    case Direction.up:
    newHead = Point(head.x, head.y - 1);
    break;
    case Direction.down:
    newHead = Point(head.x, head.y + 1);
    break;
}

// Check for collisions
if (newHead.x < 0 || newHead.x >= gridSize || newHead.y < 0 || newHead.y >= gridSize || snake.contains(newHead)) {
    endGame();
    return;
}

setState(() {
    snake.insert(0, newHead);
    if (newHead == food) {
    score += 10;
    generateFood(); // New food
    } else {
    snake.removeLast(); // Remove tail if no food eaten
    }
});
}
6

Build Game Interface (UI)

Design the game board UI with controls for starting and ending the game.

πŸ’‘ Tip: Use the button on the right side to easily copy the code.

@override
Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
        title: Text('Snake Game - Score: $score'),
        backgroundColor: Colors.green,
    ),
    body: Center(
        child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
            buildGameBoard(),
            buildControlButtons(),
        ],
        ),
    ),
    );
}

Widget buildGameBoard() {
    return Container(
    width: MediaQuery.of(context).size.width * 0.8,
    height: MediaQuery.of(context).size.width * 0.8,
    decoration: BoxDecoration(
        border: Border.all(color: Colors.green, width: 2),
        color: Colors.white,
    ),
    child: CustomPaint(
        painter: SnakePainter(snake, food, gridSize),
    ),
    );
}

7

Create Control Buttons for the Game

Add directional controls and buttons to start or end the game.

πŸ’‘ Tip: Use the button on the right side to easily copy the code.

Widget buildControlButtons() {
    return Column(
        children: [
        ElevatedButton(
            onPressed: isGameRunning ? null : startGame,
            child: Text('Start Game'),
        ),
        SizedBox(height: 20),
        Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
            controlButton(Icons.arrow_upward, Direction.up),
            Row(
                children: [
                controlButton(Icons.arrow_back, Direction.left),
                SizedBox(width: 50),
                controlButton(Icons.arrow_forward, Direction.right),
                ],
            ),
            controlButton(Icons.arrow_downward, Direction.down),
            ],
        ),
        ],
    );
    }
    
    Widget controlButton(IconData icon, Direction dir) {
    return ElevatedButton(
        onPressed: () {
        if (isGameRunning && direction != dir.opposite) {
            setState(() => direction = dir);
        }
        },
        child: Icon(icon),
    );
    }
8

Draw the Snake and Food on the Canvas

Create SnakePainter, a CustomPainter, to visually draw the snake and food on the grid.

πŸ’‘ Tip: Use the button on the right side to easily copy the code.

class SnakePainter extends CustomPainter {
    final List<Point> snake;
    final Point food;
    final int gridSize;
    
    SnakePainter(this.snake, this.food, this.gridSize);
    
    @override
    void paint(Canvas canvas, Size size) {
        final cellSize = size.width / gridSize;
        final paint = Paint();
    
        // Draw food
        paint.color = Colors.red;
        canvas.drawRect(
        Rect.fromLTWH(food.x * cellSize, food.y * cellSize, cellSize, cellSize),
        paint,
        );
    
        // Draw snake
        paint.color = Colors.green;
        for (var point in snake) {
        canvas.drawRect(
            Rect.fromLTWH(point.x * cellSize, point.y * cellSize, cellSize, cellSize),
            paint,
        );
        }
    }
    
    @override
    bool shouldRepaint(CustomPainter oldDelegate) => true;
    }
    
    enum Direction { up, down, left, right }
    
    extension on Direction {
    Direction get opposite {
        switch (this) {
        case Direction.up:
            return Direction.down;
        case Direction.down:
            return Direction.up;
        case Direction.left:
            return Direction.right;
        case Direction.right:
            return Direction.left;
        }
    }
    }
                                
 
Step 1 of 8