Deven Joshi Deven is a Google Developer Expert for Flutter and an avid developer who loves all things mobile. Tinkering with Flutter since its early days, he started out with writing content to make Flutter even easier to understand and experiment with. When not programming, you will find him playing chess or reading something new.

Comparing Flutter game engines

6 min read 1937

Comparing Flutter Game Engines

If you’re a mobile app developer, chances are at some point you’ve looked at a really popular game and thought you should give game development a try.

I remember being surprised by the popularity of the simple but highly addictive Flappy Bird and thinking that it really wasn’t a very difficult game to build. It wasn’t nearly as complex as Clash of Clans, Monument Valley, or similar games that obviously required a much larger development team. This was several years before Flutter was released. At the time, I was still experimenting with libGDX on Android!

Over the years, I’ve enjoyed experimenting with developing games with a variety of game engines on different platforms and frameworks.

In this article, we’ll explore different ways to create games with Flutter. We’ll also compare different Flutter game engines. This article will not cover adding the Unity app to Flutter. In that scenario, the game is actually created outside the Flutter framework.

The only prerequisite for this article is a basic understanding of Flutter.

Understanding game engines

Game engines can be quite diverse in terms of the type and quality of functionality they provide. Some offer a full suite of solutions, while others have a much narrower offering. Still, all game engines need to address or compensate for a game’s dynamic nature, handling of a large number of objects, and limited interaction with the underlying UI components.

Constantly changing

Games are generally very dynamic. They may need to account for scrolling backgrounds, bouncing objects, and even button smashing by confused or frustrated users. For good performance, a game needs to update as quickly as possible without requiring a setState(() {}), such as a callback to render a new frame.

The critical code that makes this possible is the game loop. The game loop runs over and over, allowing a game to update object positions, resize the screen, change the camera location or perspective, and more.

Game loops are present in most, if not all, game engines. This is a key difference from the underlying Flutter framework, in which new frames are drawn after an event occurs rather than having a continuously updated canvas.

Involve multiple objects

Most games have an abundance of visual objects, and some even use fancy particle effects. These objects use a lot of memory, so game engines generally provide methods to load and dispose of them at appropriate places. These techniques are somewhat similar to the initState() and dispose() calls of the normal Flutter framework state.

Independent of underlying components

Games don’t generally use the UI components of the underlying framework. For example, you can’t use Flutter widgets to build game objects in most engines. Game engines usually render on a canvas. This allows for a large number of objects to be on the screen at once. It also enables a game to be ported uniformly across platforms, since nearly all operating systems support canvas rendering.

Visual game objects are usually referred to as sprites. These can be static or animated and may be created in a myriad of ways. One example is a sprite sheet. This is essentially one large image file consisting of multiple sprites or versions of a sprite. The individual images may be edited and reordered to give an illusion of movement. The sprites can be PNG images that are rendered onto the base canvas.



Other features (such as audio, gesture detection, and cameras) usually vary much more from one game to the next compared to the basic graphics.

Creating games without an engine

Can you create a game without an engine? Yes. Should you? In most cases, no. Here’s the short answer for why you shouldn’t: absolute math hell.

To clarify further, I believe everyone should try creating a full game (at least once) without any help from an engine. This is hard work. It will essentially involve writing a basic engine from scratch.

It’s understandable to be cautious about attempting a project of this scale, but it can be very instructive to attempt your own implementation. Diving into the elements of a game system can provide invaluable experience.

Flutter can be used to create the basic components of a game. Tickers and timers may be used to create a game loop. You can create a positioning system, build a sprite system, make calculations for collisions, and add your own gesture detection according to element positions. Of course, none of this will be easy. But, it could be a fun exercise.

Game engines have a distinct advantage when it comes to building complex elements or features. Any game that requires advanced features (such as hitboxes, physics, audio support, and camera movement) will be much easier to design using a solid engine rather than trying to code it from scratch.

Flame

Flame is a complex, mature game development framework and is currently the most popular Flutter game engine. It supports everything needed to design a basic game, including a game loop, sprites and sprite sheets, collision detection, and audio. Flame also offers several complementary packages for more complex functionality, such as enhanced audio support, physics, SVG support, and Rive integrations.

Flame uses a GameWidget to add a game to an app:

GameWidget(
 game: game,
 loadingBuilder: (context) => const Material(
   child: Center(
     child: CircularProgressIndicator(),
   ),
 ),
 errorBuilder: (context, ex) {
   return Material(
     child: Text('Error'),
   );
 },
 overlayBuilderMap: {
   'victory': // Build victory overlay,
   'defeat': // Build defeat overlay
 },
),

The overlayBuilderMap argument allows us to neatly define any overlays that may be needed throughout the course of the game, such as a victory, defeat, or pause menu. The loadingBuilder and errorBuilder arguments can be used to let users know the game is loading or to provide an error message, as needed.

We can define the game itself, DemoGame, by extending the FlameGame class:

class DemoGame extends FlameGame {

  @override
  Future<void> onLoad() async {
    // Load sprites, camera, etc.
    return super.onLoad();
  }

}

We define capabilities and functionality with mixins:

class DemoGame extends FlameGame with
HasCollidables,
HasTappables,
HasDraggables {

  @override
  Future<void> onLoad() async {
    // Load sprites, camera, etc.
    return super.onLoad();
  }

}

To create game objects, we subclass any component type that has a position and size. In this example, we subclass PositionComponent:

class DemoComponent extends PositionComponent with Hitbox, Collidable {
 
 DemoComponent({
   required Vector2 position,
   required Vector2 size,
 }) : super(position: position, size: size);

 @override
 Future<void> onLoad() async {
   await super.onLoad();
   // Initialize sprites, hitboxes
 }

 @override
 void render(Canvas canvas) {
   super.render(canvas);
   // Render objects
 }

}

We can also use Flame’s Forge2D package to add Box2D physics to the engine. This package provides the functionality to build more intricate games, featuring objects with more realistic movement.

How Flame compares to other engines

Personally, I like the code structure of the Flame engine components and the neatly separated game logic. Flame offers several types of game objects, as well as various mixins that supply different kinds of functionality. Flame provides thorough documentation and multiple tutorials on its website and in its README file.

Quill

Quill is a lightweight game engine that uses simple components, cleverly referred to as Feather and Quill, to render game objects.

To design a game with Quill, we start by initializing a new QuillEngine and supplying a Feather containing the game engine loop for initialization, update, and disposal.

void main() async {
  QuillEngine(Demo())..start();
}

Next, we extend the Feather class to get the game loop:

class Demo extends Feather {

  @override
  void init() {
    // Initialize 
  }

  @override
  void input(Event event) {
    // Handle input
  }

  @override
  void update(Time time) {
    // Update objects on new frame
  }
}

We can create a Sprite() inside the Feather subclass:

  Sprite _demo;

    _demo = new Sprite()
      ..initWithColor(const Color(0xFFFFFFFF))
      ..setPosition(0.0, 0.0)
      ..setSize(100.0, 100.0);

How Quill compares to other engines

Quill is far less complete compared to Flame. There are several missing features, like audio and image caching, that are listed in the engine’s documentation as being slated for a future version. Additionally, Quill’s game objects appear to have less code separation compared to other engines.

SpriteWidget

SpriteWidget is a toolkit that can be used to create animations and games in Flutter. This package works well with the widget hierarchy making it feel much more Flutter-like (or “Flutter-y”) compared to other game engine solutions.

SpriteWidget can be used to create both sprite nodes and node graphs, making for some really interesting possibilities. For example, the toolkit’s documentation describes creating a car from different sprites and linking wheels to the base car node through offsets. SpriteWidget also contains comprehensive animation techniques, including sequences and grouping multiple animations.

How SpriteWidget compares to other engines

SpriteWidget offers several useful techniques and provides a unique solution for handling many aspects of game development. However, it does not offer a full suite of game development tools and also has not been well maintained. The pub scores at the time of this article reflect the toolkit’s resulting degradation.

Illume

I recently created a small demo game engine of my own: Illume. Illume uses Flutter widgets as game objects and adds a simple game loop.

While researching Flutter game development engines for this article, I noticed that most of the solutions rely on the technique of adding sprites to a canvas. This is probably the most rational and permanent solution for a game engine, but I wanted to try to leverage Flutter’s “everything is a widget” philosophy.


More great articles from LogRocket:


I wanted to build an engine that would mesh better with a normal Flutter app, rather than being entirely separated from the main widget UI code. To some extent, SpriteWidget achieves this, but technically it uses wrappers rather than widgets.

To build a game with Illume, we simply use the Illume widget with an IllumeController argument, which controls different aspects of the game:

IllumeController gameController = IllumeController();

// Inside build
Illume(
    illumeController: gameController,
),

To define game objects, we extend the GameObject class. For example, we can use the following code to initialize walls:

class Wall extends GameObject {
  int initialDistance;

  Wall(
    this.initialDistance,
  );

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      child: const Text('Demo'),
    );
  }

  @override
  void init() {
   // Init size, alignment, position, etc
  }

  @override
  void onCollision(List<Collision> collisions) {
    // Called when collisions occur
    // E.g: illumeController.stopGame();
  }

  @override
  void onScreenSizeChange(Vector2 size) {
    // Transform object positions on screen changed
  }

  @override
  void update(Duration delta) {
    // Update on new frame
  }
}

GameObject provides access to position, alignment, and basic box-based collision detection for every object, triggering a callback when a collision occurs. The build method allows us to create an object directly in the Flutter widgets. We can even use Flutter’s default gesture detection widgets.

How Illume compares to other engines

Illume is not meant to be a replacement for mature game engines. Widgets are heavier than sprites drawn on canvas, so Illume takes more memory to run and currently lacks the functionality for complex game features. Illume does, however, provide an easy solution for building a simple game quickly using widgets.

Conclusion

In this article, we explored how game engines differ from the underlying framework. We also discussed the pros and cons of coding a Flutter game from scratch or using one of the following game engines: Flame, Quill, SpriteWidget, or Illume.

Flame is currently the only fully developed, well-maintained solution available for creating games on Flutter. Other game engines offer creative techniques but have not yet developed to a point where they are viable to use for full-fledged games. I think any of the solutions described in this article are appropriate for smaller games, but I would recommend Flame for development of larger production games. At present, game development in Flutter is still pretty much a one-horse race.

: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

.
Deven Joshi Deven is a Google Developer Expert for Flutter and an avid developer who loves all things mobile. Tinkering with Flutter since its early days, he started out with writing content to make Flutter even easier to understand and experiment with. When not programming, you will find him playing chess or reading something new.

One Reply to “Comparing Flutter game engines”

  1. Very nice walk through!
    Flame v1.1 has been released since you wrote the article too so now it is even more stable and we have a much nicer and efficient collision detection system.

Leave a Reply