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 -> lib/good.generated/
[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 ./lib/good.generated/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)
stripped 1 loose asset(s) now carried in chunks; `good assets compact` rebuilds them
[4/4] flutter build windows
Built windows.
The shipped bundle contains the chunks and not the loose files — each asset ships exactly once:
build/windows/x64/runner/Release/
├── my_game.exe
├── flutter_windows.dll
└── data/flutter_assets/assets/
└── packed/chunk_shared.dat ← the assets, compressed and encrypted
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 |
| 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. -
lib/good.generated/asset_key.dartis committed — 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:. -
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.