In this tutorial, we’ll build the Snake Game with Flutter. Some interesting things we’ll cover:
- Object-Oriented Design
- No frameworks - No game engines or third party libraries
- Flaws and Future Follow Ups
Things that aren’t covered well:
Structuring a Flutter project- We’ll do it in one single file (executable on dartpad.dev)
UI/UX- No menu, UI, glyphs, etc.
Scoring- Because it doesn’t add value to project.
For any simple single-player game, I usually start by thinking the following, step by step:
Objects: What are the moving parts of the game, and which objects matter for building the MPG (Minimum Playable Game). In our game, they are the Snake and it’s food.
State: The state of the game and connecting the objects. In the snake game, the state is a grid, wherein the snake and food will be. We’ll draw these using simple containers.
Update State: The snake has to continually move forward which is a periodic update, and based on this (and the user input) the objects will have to respond to different states like snake eating the food, snake biting itself, snake hitting the wall, changing direction, etc.
Controls: User controls; In our case, change the direction of the snake based on input (arrow keys to set the direction).
Score: Score calculation and reset - number of food eaten. (not implemented in this tutorial though)
So having these things sorted, let’s code!
We start with a stateless widget
SnakeGame to define our
MaterialApp. Followed by a stateful widget
Game which would render our game and manage the
Since we’ll need a few constants, let’s define a
Constants class to access/modify them easily. Right now, we need 2 constants,
canvasSize (height and width of the canvas) and
blockSize (size of 1 block) so that we can imagine our canvas as a grid of side
After replacing height and width in the
Constants.canvasSize we have a 50x50 imaginary grid to work with.
Let’s start with our Snake; To represent the snake we’ll use a List of 2D points, and the number of points increases when the snake eats food. The first element of the list will be the location of the head and the last element, the tail. A point is just x, y values on a grid and for that, we’ll use dart’s
Point class (imported from
We also need a
direction to represent in which direction the snake is moving, this can also be represented using a
Point object since we just need to update the
head of the snake along a certain axis, i.e. certain x, y values. We can add these directions to the constants and define our snake:
Now, we can add a snake object to our
GameState along with the food, which also can be just a
Point. In the below code, we introduce some things:
random: To generate random numbers (random food locations).
initGame: To initialize the Snake object and food location.
foodUpdate: To update food location.
Now we are more or less done with the foundation code, we just need to paint the snake and food on the canvas. For that, we’ll use
Positioned puts a widget to provided locations in a
Stack. We’ll use the properties,
left to set
x of the widget respectively.
In the above code, we basically created a Positioned Widget for each part of the snake and added a food object as well to that list.
Let’s make the game alive now. We want to periodically update the state of the canvas and for that, we can use
Timer.periodic (imported from
dart:async) which calls a function after certain
We can add it to the
initState which gets called once our game starts. We also need an update function, which is passed as a callback to
Timer.periodic and is responsible for updating the game state.
Before implementing this
gameUpdate function, let’s see what we actually need to update:
- Move the snake in a particular direction
- Check if the snake’s head overlaps with food
- if yes, eat food, add length to the snake, and call
- if yes, eat food, add length to the snake, and call
- Check if the snake’s head overlapped with any other body part, if yes, reset the game.
Implementation (added to GameState):
Added to Snake Class:
At this point, our game starts working (not controllable though). One bug I would like to address is that our
foodUpdate might add the food on the snake’s body, so let’s add a check for that.
Another interesting thing in the above code is
snake = Point((head.x + direction.x) % 50, (head.y + direction.y) % 50); this line is really interesting on how it handles negative x, y cases (read about mod with negative numbers).
We are pretty much done now, we just need to be able to control the snake, that’s it. I understand the chronology of this is a bit weird and we should have done this before the whole eat food logic, but having implemented the snake game quite a bit, I usually do this at the end.
This part doesn’t need much explanation, just two things are sufficient I guess:
- We are using
autofocusset to true (so that we don’t have to tap it to take control). We’ll also define a
keyHandlerfunction to handle the events by the listener. (
LogicalKeyboardKeyis imported from
- Two edge/additional cases I would like to address:
- We don’t allow the snake to go in the direct opposite direction.
- We don’t call
setStatebecause regardless, it would be called within max
50 ms; We CAN call it but it won’t make much of a difference.
Voila! Our game is ready. Try it below:
- This implementation still has a few IO bugs which can lead to snake dying (hint: the way we load frames and update direction). We can use an input queue to fix that.
- Haven’t implemented score because it’s trivial for this game.
- A good follow up project would be to implement a multiplayer version of this.
This project is quite simple and probaly not blog worthy, but I wrote it anyways because, it was fun and I wanted an easy writeup to get out of my writing rut:)