Flutter, React Native & Unity

flutter build apk vs appbundle: Pick the Output for the Distribution Channel

Use a Flutter APK when you need an installable Android package for an appropriate distribution or test workflow. Use an Android App Bundle for a Play submission that requires or supports device-specific APK generation. An AAB is not installed directly like an APK.

By , FounderUpdated 4 min read

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

BASH · REFERENCE EXAMPLE
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

  1. Flutter: Build and release an Android app
  2. Android Developers: Android App Bundles

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.

APKLint

Android inspection tools and practical release guides. About APKLint · Report a correction

Treat every result here as one checked input into a release decision, not the whole decision. Analyzer findings describe what is verifiable from the artifact itself: the manifest, the package structure, the certificates and the bytecode surfaces. Confirm anything load-bearing against the actual build you ship, because a source tree and a packaged APK can tell different stories.

When a finding surprises you, reproduce it before fixing: change one thing in the project, rebuild, and scan again. The delta between runs is the fastest way to prove a fix landed — and a result that does not move when the input changes means the input was not what you thought it was.

Coverage notes matter as much as findings: what was inspected, what was skipped, and what was flagged in the inspected part. A clean result over a partially-scanned file is weaker evidence than a flagged finding over a fully-scanned one, so read the coverage line first. For anything touching policy, certificates or third-party SDKs, close the loop against the Android developer documentation and the Play policy center — tools speed the review up, they do not replace it.

Reproducibility is the bar for anything in this guide: if you cannot reproduce a finding on the current artifact, do not act on it yet. Change one variable, rebuild, scan again, and only trust the delta you can explain.