Exporting a game¶
Shipping a good game is one command:
It runs four steps in the order that makes them correct, and the order is the whole reason the command exists:
[1/4] compacting assets source art -> one canonical format per kind
[2/4] generating bindings canonical -> my_game_bundle/lib/
[3/4] packing assets canonical -> compressed, encrypted chunks
[4/4] flutter build windows everything on disk -> an application
Compaction writes the canonical files. Generation reads those and emits the
enums, so it has to follow. Packing reads the generated key material and writes
the chunk mapping back into the same file, so it follows generation.
flutter build bundles whatever is on disk by then, so it goes last.
Running these by hand in the wrong order does not fail
It produces a build that is subtly stale, not one that errors — which is exactly the mistake this command exists to remove.
What a release build produces¶
$ good build windows
good build windows
project: .
assets: release
encryption: aes
compression: normal
[1/4] compacting assets
0 written, 1 up to date, 0 failed.
[2/4] generating bindings
Wrote ./my_game_bundle/lib/textures.dart
1 texture(s), 0 audio file(s).
[3/4] packing assets
1 asset(s) in 1 chunk(s), grouped by scene (1 scene(s); 0 asset(s) shared or unattributed)
[4/4] flutter build windows
Built windows.
The shipped bundle contains the chunks and the loose files, so a packed asset is in there twice:
build/windows/x64/runner/Release/
├── my_game.exe
├── flutter_windows.dll
└── data/flutter_assets/assets/
├── sprites/player.webp ← the loose copy, still legible
└── packed/chunk_shared.dat ← the same assets, compressed and encrypted
strip-originals: true under good: assets: deletes the loose copy of every
packed asset, and stops Image.asset from resolving those paths. It is off
unless a project sets it. See
Both copies ship by default.
The three pages¶
-
Compaction, chunking, compression, encryption, and why the order is compress-then-encrypt.
-
Every target, the options that matter, and what to do about signing and packaging.
-
The failures the pipeline can produce, and what each one means.
Development versus release¶
The two modes want opposite things, and both are first-class.
| Development | Release | |
|---|---|---|
| Assets | Loose files | Chunked, and still loose unless strip-originals |
| Compression | None | gzip |
| Encryption | None | AES-256-GCM |
| Asset mapping | Empty | Path → chunk |
| Resolution | Straight through rootBundle |
Through the installed AssetPack |
| How you get it | flutter run |
good build <platform> |
Development leaves everything alone. The shortest path from a changed file
to seeing it is not to touch it, so flutter run loads loose files and nothing
is packed at all.
Nothing in your game code differs between the two. The same logical path
resolves through rootBundle in one and out of an encrypted chunk in the other.
good build defaults to release because flutter run is the development path
and does not come through here — someone typing good build windows is making
something to ship. Pass --assets=development if you want a platform build
with loose assets, for a debugging build you intend to poke at.
Before you ship¶
-
flutter analyzeis clean. - The bundle package is committed —
asset_key.dartinside it is the only record of your encryption keys, and losing it orphans every pack already shipped. - Both
assets/andassets/packed/are listed underflutter: assets:. -
strip-originalsis set the way you meant — left off, the bundle carries a legible copy of every packed asset. -
ensureGameReady()is called beforeGame.start— it catches a missing asset before the first frame instead of mid-scene. - A release build has actually been run and launched, not just compiled.