skip to content

In Flutter, how do you measure what makes up a release app's size, and why are debug builds and upload packages misleading?

level: middleimportance: must knowfreq 48%

answer

  1. release builds only
  2. one Android ABI at a time
  3. a code-size-analysis JSON file
  4. treemap, dominator tree, call graph
  5. stores split the upload package

basics

~20 s

Build a release with --analyze-size (one ABI on Android), read the terminal summary, then open the generated code-size-analysis JSON in the DevTools app size tool. Debug builds carry JIT overhead, and upload packages hold content the stores strip per device.

solid answer

~40 s

Run a release build with `--analyze-size`, for example `flutter build apk --analyze-size --target-platform android-arm64`; it works for `apk`, `appbundle`, `ios`, `macos`, `windows` and `linux`, refuses non-release builds and multiple Android ABIs, and cannot be combined with `--split-debug-info`. The tool prints a summary by category (assets, native code, Dart AOT code per package) and writes a `*-code-size-analysis_NN.json` file under `~/.flutter-devtools/`, with the `dart devtools --appSizeBase=...` command to open it. In the DevTools app size tool the treemap shows where bytes go and the dominator tree shows why code is kept. Debug builds are JIT builds with hot-reload overhead, so their size means nothing; and an APK, app bundle or iOS archive is an upload package the stores reprocess per device, so use the stores' own reports for real download size.

code

bash · 7 lines
bash
# Release build for one Android ABI, with a size breakdown.
flutter build appbundle --analyze-size --target-platform android-arm64

# The tool prints where it wrote the analysis file, for example:
#   ~/.flutter-devtools/aab-code-size-analysis_01.json
# and the command to open it:
dart devtools --appSizeBase=$HOME/.flutter-devtools/aab-code-size-analysis_01.json

go deeper

for a junior

Know that debug builds are not representative and that --analyze-size on a release build gives the real breakdown.

for a middle

Explain the parts of an app's size, the flag's constraints (release, one Android ABI, no split-debug-info) and how to open the file in DevTools.

for a senior

Separate upload-package size from per-device download size, and use the dominator tree to trace growth back to the import that caused it.

for a principal

Turn size into a tracked number per release, with the analysis file kept as an artifact so any growth can be diffed and owned.

## What makes up a Flutter app's size A release Flutter app is self-contained. Its size comes from a few distinct parts: - **Dart AOT code**: your code and every Dart package, compiled ahead of time into a native library (on Android, `libapp.so`). - **The Flutter engine**: the renderer and runtime (on Android, `libflutter.so`). - **Assets**: images, fonts, JSON and anything else listed in `pubspec.yaml`, including assets packages bring along. - **Native plugin code**: Kotlin, Java, Swift or Objective-C code and any native SDKs your plugins pull in. Size questions start with finding which of these grew. ## Why the easy numbers mislead | Artifact | Why it misleads | |---|---| | Debug build from `flutter run` | JIT-compiled with hot reload and debugging support; far larger than release | | `flutter build apk` or `appbundle` output | An **upload package**: may hold several ABIs and every asset density; the store splits it per device | | iOS `.app` from `--analyze-size` | Useful for relative breakdowns; not the downloaded IPA | The Flutter docs send you to the stores' own reports for the real figure: the Play Console's app size view for Android and Xcode's App Thinning Size Report (or App Store Connect) for iOS. ## Breaking the size down with --analyze-size 1. **Build release with the flag**, for example `flutter build appbundle --analyze-size --target-platform android-arm64`. Supported on `apk`, `appbundle`, `ios`, `macos`, `windows` and `linux`, not on `web`. 2. **Respect its constraints.** The tool exits with `"--analyze-size" can only be used on release builds.`, refuses Android builds for more than one ABI ("Cannot perform code size analysis when building for multiple ABIs"), and cannot be combined with `--split-debug-info`. On iOS only arm64 symbols are analyzed. 3. **Read the terminal summary.** It breaks the package into categories (assets, native libraries, Dart code) and splits the Dart AOT library by package. 4. **Find the JSON.** The tool writes `apk-code-size-analysis_01.json` (or `aab-`, `ios-`, `macos-` and so on) under `~/.flutter-devtools/` and prints `dart devtools --appSizeBase=<file>`. Intermediate files go under the build directory unless you set `--code-size-directory`. ## Reading it in DevTools The **app size tool** has two tabs: - **Analysis**: one size file. The **treemap** and table show where bytes go, down to individual functions in the Dart AOT artifact. The **dominator tree** answers "which import is the root reason this code is here?", and the **call graph** shows every path to a piece of code. - **Diff**: an old and a new size file from the same app, showing only what changed, the tool for "what grew in this release?". ## What to do with the answer - Large **assets**: compress images, drop unused files and resolutions. - Large **Dart packages**: use the dominator tree to see which of your imports keeps them, then replace or trim. - Large **native code**: check which plugin brings a native SDK. - Platform-only code: guard it with `Platform.isWindows`-style checks so the compiler drops it from other targets.

  • Why does flutter build apk --analyze-size fail without --target-platform?
    A default APK build targets several ABIs, and the size analyzer works on one AOT snapshot at a time. The tool exits with 'Cannot perform code size analysis when building for multiple ABIs' and asks for one of `android-arm`, `android-arm64` or `android-x64`. Pick the ABI most of your users run, usually `android-arm64`.
  • In the DevTools app size tool, when do you use the dominator tree instead of the treemap?
    The treemap tells you what is big. The dominator tree tells you why it is there: it shows, for a package or function, the node every path to it goes through, often one import in one of your files. That is what you change to remove it.

saying these in an interview costs you the question

  • The size of a debug APK is close to what users download.
  • The app bundle's file size equals the Play Store download size.
  • --analyze-size works on any build mode, including debug.
  • --analyze-size can analyze all Android ABIs in one build.
  • Assets never show up in the analysis because it only covers Dart code.