Making 2048 Game in Flutter by using Explicit Animations

Angjelko
6 min readJun 9, 2022

--

2048 Game in Flutter Logo

The source for the project is at Github:
https://github.com/angjelkom/flutter_2048

While exploring ideas for my next Flutter Project I’ve decided to make the famous 2048 Game in Flutter.

Now keep in mind that the purpose of the project and this post is not to learn “How to make a game in Flutter” but “How to implement AnimationWidget and Explicit Animations, manage and control them using AnimationController and state management solutions like Riverpod”. So the purpose of this project is to explore and learn how to implement, use and control animations in Flutter together with a standard state management solution like: Provider, Riverpod, Bloc and etc.

Actual gameplay of the 2048 Game made in Flutter running at 60FPS on Samsung Galaxy S10+

The above GIF is running at 33FPS so almost half the FPS the game is actually running on the phone, so running the actual game on a real device it’s even smoother than what the GIF demonstrates, also the GIF is not fast-forwarded

Let’s get started. I recommend using the Visual Studio Code as editor.

Create a new Flutter project.

Make sure you have the Flutter extension installed. Open the command palette and create a new Flutter Project:

Press CTRL + Shift + P (Windows/Linux) or CMD + Shift + P (Mac)
Creating new Flutter project using vscode

Select where to save the project and give it a name.

Project Structure

First let’s structure our project. I always use this project structure which persists under the lib folder:

Project Structure
Project Structure

Components

  • Here I hold the ui components for the app, that can be a custom Button, custom AppBar, or some other custom UI, to make it easier to decide, if the constructed component/widget will be used in another widget along with other widget it means it belongs under components

Managers

  • Here I hold the state, those that use the Provider package, name this folder providers, I like to call it managers (as it contains code that manage my state)

Models

  • Here I hold all the model classes, for example a User class would go here.

Screens

  • We won’t need this folder for this project, but I’ve mentioned it as a bonus tip, here I keep my Screens/Pages I use for my Navigation.

Const

  • Here I keep some constant values like colors, dimensions, endpoints and etc.

Repositories (or apis)

  • Again we won’t need this folder for this project, but here I keep the services that retrieve data from an API or backend

NOTE: During the duration of this project when creating files if the folder is not pointed out it means it will directly go inside the lib folder or the mentioned folder is being created inside the lib folder, so all of our code will be put inside the lib folder.

Dependencies

First we will add all the dependencies we will use for this project.

Project dependencies

Adding UI

Before we begin adding our ui for the game let’s see what components does the 2048 have. The screenshot bellow is the final look of the game we will make.

The final game in Flutter.

First we can split the game into two types of components, game components and ui control components.

Now under the ui control components we have:

  • New Game Button starts a new game
  • Undo Button reverts one step before

Now under the Game Components we have:

  • Tile Widget that’s the numbered square that will move, change number and color.
  • Empty Board Widget that’s the 4x4 board of tiles on which the tiles will be moved
  • Score Widget that displays the current score and best score
  • Game Over/Won Widget which will be shown in case the user looses or wins the game.

First lest add our const values like colors which will be used in the game, so under the const folder, create a colors.dart file and add the following colors:

cost/colors.dart

Next we will add our main widget which will contain all the other ui components, so under the lib folder create game.dart file:

game.dart

Here we use Riverpod Stateful Consumer Widget in order to access the ref object which will allow us to access the StateNotifier which you will add later.

Next edit the main.dart file and replace the content with:

main.dart

Top Down:

the WidgetsFlutterBinding.ensureInitialized(); is added on top in order to ensure that Flutter is initialised before we can lock the app to portrait mode using the SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); method.

I’ve also wrapped the MaterialApp widget inside a ProviderScope widget in order for the StateNotifiers I will add later, be accessible across all of the widgets.

Next let’s add the Score board under components, create new file score_board.dart and add the following content:

components/score_board.dart

So again here we will use the Riverpod ConsumerWidget which is same as the StatelessWidget but it provides us access to the WidgetRef which is passed as the second argument in the build method.

Now that we have the score board let’s add it to the game widget, so open game.dart and replace //TODO: Add the score board with:

Adding the ScoreBoard widget.

And make sure you have the score_board.dart component imported on top:

game.dart imports

If we run the app now we will see:

Initial Game UI

Next we will add the Undo and New Game button.

Under components folder create new file button.dart and paste the following code:

components/button.dart

This button widget will be used for all the buttons in our game, so for the Undo button, Restart button, New Game button and Try Again button.

Now lets add the Undo and Restart buttons in the Game widget, so open the game.dart file and replace:

//TODO: Add the Undo button
//TODO: Add the New Game button

with:

Adding the button widget in game.dart

And don’t forget to update the imports:

game.dart imports

When you run the app you will see:

Basic Game UI

Next let’s add the empty board on which the tiles will be shown.

under components create a new dart file empty_board.dart and add the following code:

components/empty_board.dart

I’ve decided to use Stack widget instead of GridView widget as the similar approach will be used when rendering the actual tiles.

So this basically renders an empty 4x4 board which will be build once when the app is opened and never again.

Now open game.dart and replace //TODO: Add the Empty Board Widget

with:

Adding the EmptyBoardWidget in game.dart

And again make sure the widget is imported:

game.dart imports

If we run the game now we will see the empty board and the widgets above it nicely aligned:

2048 Game UI

Thanks for reading so far, that’s all for this part, head over to the second part by clicking the link bellow.

--

--