Build & Release
Three build modes
Section titled “Three build modes”Flutter compiles the same code three different ways depending on what you need:
flowchart LR debug["debug JIT · hot reload · asserts on (slow, dev only)"] profile["profile AOT · perf tools on (measure real speed)"] release["release AOT · asserts off · optimized (ship this)"]
- Debug — JIT-compiled, hot reload, assertions on, service extensions available. Slow and large; for development only.
- Profile — AOT-compiled like release, but with performance tooling still attached. This is where you measure real-world speed and hunt jank (never profile in debug — the numbers are meaningless).
- Release — AOT-compiled, assertions stripped, debugging disabled, tree-shaken and optimized. This is what users get.
JIT vs AOT
Section titled “JIT vs AOT”The debug/release split is really a compilation split:
- JIT (Just-In-Time), used in debug, compiles Dart as it runs — which is what makes hot reload possible (it can swap in new code on the fly).
- AOT (Ahead-Of-Time), used in profile and release, compiles Dart to native machine code before the app ships. The result is fast startup and predictable performance, at the cost of no hot reload.
Tree-shaking happens in AOT builds: unused code (and, notably, unreferenced icon glyphs) is stripped so the binary stays small.
Building for each platform
Section titled “Building for each platform”flutter build apk --release # Android APK (direct install/testing)flutter build appbundle --release # Android App Bundle (Play Store)flutter build ipa --release # iOS archive (App Store)flutter build web --release # web bundleEach target produces a different artifact from the same Dart source — the framework abstracts the platform, the build step materializes it.
Flavors: one app, many environments
Section titled “Flavors: one app, many environments”Flavors (Android) / schemes (iOS) let one codebase produce dev, staging, and production builds with different config — API endpoints, app IDs, icons — without branching your code.
flutter run --flavor dev --dart-define=API_URL=https://dev.api.example.comflutter build apk --flavor prod --dart-define=API_URL=https://api.example.com--dart-define passes compile-time constants your code reads via String.fromEnvironment — the clean way to inject per-environment values without committing secrets into source.