Choose by destination
| Destination | Typical output |
|---|---|
| Direct device test | APK |
| Supported non-Play distribution | Format required by that store/channel |
| Google Play app-bundle release | AAB |
| Device-specific bundle testing | APK set generated from the AAB |
The Flutter deployment guide and Android App Bundle guide explain the output roles.
Use explicit commands
flutter build apk --release
flutter build appbundle --release
For per-architecture APK distribution, Flutter documents --split-per-abi. That produces separate architecture APK outputs; it is not the same as manually distributing a base APK without its required configuration splits.
Keep configuration equivalent
Compare the same flavor, entry point, version, signing configuration and dependencies. Otherwise, differences in size or behavior may come from build configuration rather than the packaging format.
Record the output path printed by the build. Do not upload a stale artifact from an earlier run.
Test the delivered package
A locally installed APK and a Play-generated device APK can differ in signing and delivered resources/native libraries. Verify sign-in, billing, plugins and asset loading through the intended distribution path.
An AAB accepted by an upload interface has not yet proven that every supported device receives a working installation.
Inspect without changing signed output
Use APKLint's Flutter and AAB tools to review package identity, structure and relevant metadata. Do not unzip, delete files and rezip a signed release as a size optimization; rebuild from source with supported settings.
Keep the AAB/APK, build logs and symbols together. The format choice should follow distribution requirements and testing needs, not a blanket belief that one extension automatically makes the app faster or more secure.
Build equivalent artifacts before comparing
Suppose a project has a staging flavor whose entry point is lib/main_staging.dart. Specify both for each command so the APK and AAB represent the same app configuration. These commands assume that flavor already exists in the Android project; they do not create it.
flutter build apk --release --flavor staging --target lib/main_staging.dart
flutter build appbundle --release --flavor staging --target lib/main_staging.dart
Record the output paths printed by the two commands. Check the application ID and version code inside each result rather than deciding by filename alone. If the staging flavor adds a suffix to the application ID, it belongs to that staging app identity. Uploading it to a production Play entry with a different package name is a configuration error, not a packaging-format limitation. Flutter's Android deployment guide documents release builds and signing.
Choose the installation path deliberately
An APK can be installed directly on a compatible Android device. An AAB is an input to a distribution process that generates installable APKs; it is not an APK renamed with another extension. For local bundle testing, use bundletool to generate an APK set for the intended device, or test through the intended Play track. Keep all required splits together when installing a split set.
For an explicit per-ABI APK build, flutter build apk --release --split-per-abi produces separate architecture outputs. Select the one compatible with the test device. This is useful for direct distribution where you control that choice, but it also means the distributor must avoid offering an incompatible architecture to a user. A universal APK trades a larger artifact for simpler direct distribution; that does not make every byte relevant to every device.
Separate archive size from device download
Imagine a universal APK is 48 MB and the AAB is 44 MB. Those invented archive sizes are not a measured four-megabyte saving for each user. The store can deliver different language, density and ABI components to different devices. Compare the delivered package for the same device specification, and state whether your number measures compressed download, package bytes on disk or installed footprint. Avoid calling an AAB “faster” based only on the size of its upload file.
Use the same Dart entry point, compiler options, assets, dependency lockfile and signing setup for the comparison. A missing asset or a debug-versus-release mismatch can create a size difference much larger than the packaging choice. Keep the build logs so another developer can reproduce which settings created each artifact.
Exercise release identity and migration
Install the previous intended-channel release, retain its data, and update to the candidate. Check startup, persisted settings and a native plugin that depends on package or certificate identity. Then perform a clean install and repeat the same path. An app that works only with development credentials has not passed a release test simply because its Dart screens render.
Retain matching Dart symbol or obfuscation information when your build uses those features. Keep it associated with the exact version code and artifact hash. If a crash later arrives from the Play-delivered build, this record lets you inspect the actual release rather than reconstructing a superficially similar build from a newer dependency set.
Sources and further reading
Reference review: 29 September 2026. Examples illustrate the workflow; check your installed versions, release artifact and account-specific Console requirements before applying them. This guide is not a claim that APKLint executed your project or verified your private account.


