Can we build a Cross-Platform game using Flutter and Flame?

Bishwajeet Parhi
11 min readJul 10, 2022
Flutter and Flame logo

With the announcement of Flutter 2.8, the Flame engine also rolled out a stable 1.0.0 version.

flutter 2.8 reveal

But wait a minute !!!

What is Flame?

Flame is a modular Flutter game engine that provides a complete set of out-of-the-way solutions for games. It takes advantage of the powerful infrastructure provided by Flutter but simplifies the code you need to build your projects.

It provides you with a simple yet effective game loop implementation, and the necessary functionalities that you might need in a game. For instance; input, images, sprites, sprite sheets, animations, collision detection and a component system that we call Flame Component System (FCS for short).

flame homepage

So, if creating games in Flutter is possible, why not create one? The idea here is to make a game that works on all the major platforms and provides the same gaming experience.

Let’s Talk about how Flame works

In Flutter, everything is a widget, similarly, in Flame engine, everything is a component. The below image shows the basic lifecycle of how the game engine runs.

Lifecycle of Flame Engine

Let’s learn more about it while we build a game.

So, what are gonna build?

I was thinking of making an arcade/action-type game. The idea is we have a player with a weapon, we have a 2D map — ground, some floating grounds, and kinds of stuff. There would be enemies( currently 3 types) — waves of enemies and the goal is to eliminate all of them. The number of enemies would increase with each wave passing and it will go on until his health runs out. Thus we will then calculate his score. Since I was thinking of a cross-platform game, we would be having a high score board too. This was some sort of rough diagram I made 😂.

Something I thought of making with my brother

Let’s set up our project

You know what, I prepared some initial setup for you in a repo. Just clone it and we will be on the same page.

Before moving on, let’s check out the dependencies in the project which we are gonna use in this project and I will keep adding them when needed (as this isn’t gonna be a single blog😉).

As you can see I added the flame dependency. That’s important to develop games in Flutter.

I have also imported flame_forge2d and flame_audio package. We will discuss more about this later.

For some light state management, I use riverpod , it’s my all-time favorite state-management solution.

And for Backend solutions, I used appwrite — an open-source firebase alternative. It’s rising its popularity these days and I love its simplicity on how easily you can use it. To know more about appwrite:

I won’t go deeper in appwrite (I have already made a series in it, refer from here), as this would be more oriented on game development, though I would talk some things about it which when required.

Now, this branch contains all the setup — the assets we are gonna use, an initial overlay (main menu), some utility classes, and the dependencies added.

NOTE: The assets used are all taken from itch.io . And these are assets are completely free to use for commercial purposes too.

Now if you open the project, there are some TODOs that we need to complete initially. But before that, I hope you have set up appwrite and integrated into your Flutter project (refer to this site for setup).

Now Let’s Complete those TODOs

I will start first with client.dart file.

For me these are those settings, your endpoint could be different here if you set the default port. Your project Id could also be different here. Just make sure you double-check them in the appwrite Dashboard.

Now Let’s Look at the Authentication.dart file. There are 3 functions we need to complete.

  • getAccount()
  • login()
  • loginAnonymous()
  • logout()

Before moving forward, lemme tell you something. We would be using an OAuth2 for authentication. I was thinking since we are making a game, why not add a Discord OAuth here. Now the great part here is that Appwrite supports more than 20 OAuth providers and Discord is one of them.

Let’s Learn how to add setup Discord OAuth provider

STEP 1: Go to discord/developers/applications . Login if asked.

STEP 2: Click on New Application

STEP 3: Give a name to your app . For me it’s Mini Wars. For you it could be anything or same. Click Create.

You would see something like this. It’s your wish if you want to add a description and tags.

What we want here is an APPLICATION ID and PUBLIC KEY.

STEP 4: Head over to Appwrite Dashboard -> Users -> Settings

Look for Discord OAuth under OAuth2 Providers

STEP 5: Enter the following fields and copy the redirect URL

STEP 6: Now in the Discord Developer Portal — go to OAuth2 -> general and add the redirect URL there

That’s it, all the things have been done from the server-side. Let’s code now

REMINDER: Keep yourself Hydrated

Let’s Code Authentication class file

That implements all the utility functions needed for Authentication. One thing to note is that OAuth does not work in the Windows Flutter app. This is because the appwrite uses flutter_web_auth package to authenticate users.

Since this does not support Windows and Linux platforms yet, authentication is not yet possible. You can track this issue here. But it works well on other platforms (Web, iOS, Android, macOS)

Let’s complete the final function on the main_menu screen and let’s finally move on to the game logic.

And in the onPressed function of the Play Button, let’s add this for now:

widget.gameRef.overlays.remove(MainMenu.routename);

That completes our work for the time being on main_menu screen for now. Let’s talk more about Flame now

How do you add a component inside a widget?

In Flutter everything’s a widget as I mentioned in the blog above. How are you gonna run a Flame engine on top of it which needs components?

This is where GameWidget comes to the rescue. Let’s look at the code of main.dart .

To add a Game Component inside a widget, we need GameWidget<T> and thus you pass your object in the parameter. This is only required once cause we are mainly adding either FlameGame or Forge2DGame component and thus from there we manage adding or removing components.

You may have one more question here, Why not simply add MiniWars() in the parameter instead of creating a variable and then passing it?

This is because when you hot reload every time, a new object instance is always created so it kinda works like hot restart. So it’s kinda annoying when your game just relaunches again and again for some really small changes. So we have added this just for debugging purposes and would remove it in production. So it’s better to add a TODO here to not forget yourself.

Let’s create mini_wars.dart file. We will be creating a single Forge2DGame instance that manages everything for our game to run.

So Forge2DGame and not FlameGame?

Forge2D is basically a ported version of the Box2D physics engine (written in C++). Some of the features like a rigid body, gravity, continuous collision, etc come out of the box with this package. We could use FlameGame but that means writing more boilerplate. Also, I will be just experimenting here for now, and let’s see what fits better.

This is the initial mini_wars.dart file. You see that I have extended the Forge2DGame .

Now Forge2DGame has some functions which can be overridden. I would recommend just going through the source code and reading the comments for a better understanding. Seems overwhelming at first but then you would get the hang of it. Let’s talk about those methods which I would be overriding for the time being now.

onLoad() Method. As the name suggests, before late initialization, this would load all things you need (like images, audio, make some HTTP calls to fetch something, etc). This method would be only called once in a lifetime. By default, it returns null.

Here I wanted to add a mainMenuBackground before everything gets initialized. Oh yes, When the game starts, our MiniWars object is called and an initial overlay MainMenu widget is displayed. For background, we add mainMenuBackground component and then we remove it after we click play.

Let’s Look at the background_component.dart file

Flame Supports Parallax out of the box so you don’t need to hardcode it yourself😉. I had a parallax background image in 4 strips.

These 4 images combined to form a background and they need to be repeated. Thus Parallax.

So we created a map of paths with their velocity multipliers. The next thing we had to do is load all the parallax layers as done above.

Then ParallaxComponent has a Parallax object. We used the setter and added all the parallax layers with base velocity and size.

Read more about ParallaxComponent here

Let’s see how it looks so far

I will show you the outputs from 3 Platforms (Windows, Android, and web)

Windows
Web
Mobile

Well, I don’t know what to say but it looks

Now onto adding the player on the screen

I have also included the Player sprites in the repo itself. Just take a look at it. And these are all sprite sheets, i.e we will animate them. Now if you have used SFML in C++, you had to manually write some logic code for those animated sprite sheets but luckily Flame Engine does a SpriteAnimationComponent which can animate your sprites. Since we have a bunch of Spritesheet which include — jumping, crouching, idle, walking, running, etc; we need SpriteAnimationGroupComponent<T> . Thus, we will change the sheet on the component when the following action is performed. Say when jumping, we use jumping sprite sheet and so on.

Let’s work on player_component.dart file. We will first work on animating the sprite first then we will add some movement. Let’s define an enum PlayerState .

Let’s create our Player class . Just read this doc and you will understand better of how SpriteGroupComponent works.

Look at the source code carefully. Each SpriteAnimation variable denotes the sprite sheet animation to shift. It has a variable current which shows the current state to show. I have set to idle. We also added a mixin KeyboardHandler to change the animations by pressing keys.

The next thing to do is load all the sprite sheets. I created a separate function and called in onLoad() function. Then I linked it with the correct key values. Then all was left to complete onKeyEvent .

Let’s check it out now.

Don’t forget to create a variable in MiniWars and add it using the Play button onTap function.

and in mini_wars.dart file

final Player hero = Player();

Live Time

Sprite Animation

I will end this blog here. In the next part, I will talk about involving the physics of the player, gesture input for mobile devices, and maybe add some more sprites or something?

If you want to reach me, you can by the following handles:

Stay Tuned for more ✨

--

--