In this post
Every discussion about games in Flutter collapses into the same binary question: Flame or no Flame?
The binary framing is poor because the answer depends on a property of the game that is almost never named. After five shipped games, our criterion fits on one line:
Does the game state change per frame or per event?
Everything else follows.
The criterion, applied
A game that changes per event has stable state between interactions. Nothing happens until someone touches the screen: puzzles, cards, merge, turn-based and most idle games.
A game that changes per frame has a simulation running continuously — position, velocity, collision. It keeps happening with your finger still.
| Game | Changes per | Choice |
|---|---|---|
| Endless flyer with obstacles | frame | Flame |
| Merge with passive income | event | plain widgets |
| Collection of short minigames | event, mostly | plain widgets |
| Platformer with physics and levels | frame, heavy | full engine |
Note that "per event" does not mean static. An idle game accrues resources over time — but that is a function of the clock, evaluated when the screen needs to draw, not a 60-frames-per-second simulation.
The difference is not whether the game "has motion". It is whether the motion must be simulated step by step or can be interpolated between two states. Transition animation is interpolation, and Flutter does that for you.
Where plain widgets win easily
For event-driven games, using Flame adds a parallel system you do not need.
Responsive layout for free. A grid that adapts to any screen size is the problem Flutter solves better than anything. Reimplementing it inside a canvas is pure work.
Accessibility and input. Touch, drag, focus, screen readers — all already work. Inside a canvas none of it exists until you build it.
One system. Menu, store, settings and the game share widgets, theme and navigation. No bridge between two worlds.
Tooling. Real hot reload, the widget inspector, everything that already exists.
Our merge game is the clearest example: a 5×6 grid with slots that unlock by level, drag-and-drop merging, exponential passive income and capped offline gains. None of that is per-frame simulation. It is state, rules and transitions — and the UI is a responsive grid, which is the house speciality.
Where Flame wins, and wins big
When state changes per frame, writing it by hand means reimplementing four things:
- 1. A game loop with delta time. An
AnimationControllergives you a
per-frame trigger, but you still need an update model with elapsed time, or speed changes with the device.
- 2. A component tree with lifecycle. Objects that spawn, update, collide and
die. Without structure that becomes a list full of conditionals.
- 3. Collision detection. The item that looks simplest and consumes the most
time. Continuous collision across many objects is not a rectangle if.
- 4. Camera and world coordinates. Separating world space from screen space is
what enables camera, zoom and parallax.
Doing all four by hand is writing Flame with fewer testing hours behind it.
Our endless flyer sits on that side: continuous flight, approaching obstacles, bosses, collisions throughout. There is no reasonable plain-widget version of it.
The mental model that works
The confusion comes from treating Flame as an alternative to Flutter. It is not — GameWidget is a widget.
MaterialApp
├─ MenuPage (ordinary Flutter)
├─ StorePage (ordinary Flutter)
└─ MatchPage
└─ GameWidget (Flame starts here)
└─ HUD (can be Flutter again, on top)
That dissolves the false choice. You do not decide for the whole app; you decide for the match screen. Menu, settings, store and leaderboard stay Flutter in every scenario — and they are most of the screens.
When even Flame is not enough
There is a third step, and it is about tooling, not rendering.
When the game needs physics with bodies and materials, tilemaps, a scene editor and a level-authoring flow usable by someone who does not program, the conversation changes. What is missing is not a rendering library: it is an authoring environment.
The criterion is short:
Is anyone going to draw levels in this game?
If yes, you need an editor. Levels written in code are sustainable up to the third; from the tenth on it is suffering.
The mistake we made
Worth recording, because it is the most common one: starting with Flame just in case.
The reasoning sounds prudent — "what if we need motion later?". In practice the cost arrives before the benefit: you enter a custom coordinate system, lose responsive layout, and reimplement buttons and lists inside a canvas for a need that may never come.
The cheap path is the opposite. Start with widgets and move the match screen to Flame if it needs it. The migration is local, because only that screen changes.
Frequently asked questions
Does Flame replace Flutter?
No. Flame runs inside Flutter, in a widget. That is its biggest advantage: menus, store and settings screens stay ordinary Flutter, with the widgets you already know.
Can you build a game with just an AnimationController?
You can, and it is the right call more often than people think. Turn-based, puzzle, card, merge and most idle games do not need a per-frame game loop. They need state and good transitions.
When does Flame start to pay off?
When you need per-frame updates over many objects, continuous collision detection or fine camera control. If your game has those three, writing it by hand is reimplementing Flame worse.
And when is Flame not enough?
When the game needs physics bodies, tilemaps, a scene editor and level authoring tools. Then the conversation is not about a library, it is about a full engine — and leaving Flutter is on the table.