Building & Releasing
Writing and testing an app is only part of shipping it — at some point it needs to become a real build you upload to the Play Store or App Store. This lesson covers the practical steps: the difference between a debug and release build, producing an Android App Bundle, signing your app, building for iOS, versioning correctly, and a couple of small but important finishing touches like app icons.
Debug vs Release Builds
Every time you run flutter run during development, you get a debug build
by default — it includes hot reload, runtime assertions, and extra debugging information, all of
which make development pleasant but also make the app noticeably larger and slower than what you'd
actually ship. A release build strips that debugging overhead and applies
optimizations, producing the fast, small binary that real users install. You always test correctness
in debug mode day to day, but performance and final builds should be checked in release mode.
Building an Android App Bundle for the Play Store
Google Play now requires apps to be submitted as an Android App Bundle
(.aab), not a plain .apk. An app bundle contains your compiled code and
resources for every device configuration, and Google Play generates optimized, smaller APKs for each
specific device at install time — so users only download what their device actually needs.
flutter build appbundle --release
The resulting .aab file appears under build/app/outputs/bundle/release/,
ready to upload to the Play Console.
Android App Signing
Every Android release build must be cryptographically signed with a keystore — a
file containing a private key unique to you as the app's publisher. In broad strokes: you generate
a keystore file once with a command-line tool, reference its location and passwords in a
key.properties file (kept out of version control), and Gradle uses it automatically
when building a release.
Building for iOS
Building an iOS release requires a Mac with Xcode installed — there is no way around this requirement, since Apple's build tools only run on macOS.
flutter build ios --release
Unlike Android, a finished iOS build isn't simply uploaded as a file — the actual submission happens by opening the generated Xcode project, archiving the build inside Xcode, and uploading it to App Store Connect from there, where it goes through Apple's review process. If you don't have access to a Mac, this half of app distribution genuinely isn't possible to complete yourself.
Versioning Your App in pubspec.yaml
Every Flutter project's pubspec.yaml (already familiar from
Packages & pubspec.yaml) has a version
field with two parts separated by a +:
version: 1.0.0+1
| Part | Meaning |
|---|---|
1.0.0 | The semantic version shown to users on the store listing (major.minor.patch) |
+1 | The internal build number — must strictly increase with every single submission, even a minor bugfix resubmission, regardless of whether the visible version number changed |
A common mistake is forgetting to bump the build number on a resubmission — both Apple and Google will reject an upload whose build number doesn't exceed the previous one, even if it's a brand-new binary.
Building for Web Too
Flutter's "single codebase, multiple platforms" promise extends beyond mobile — the exact same project can also produce a web build:
flutter build web --release
This compiles your Dart code to JavaScript/WebAssembly and produces a static site you can host anywhere, using the same widgets and logic as your mobile app.
App Icons and Splash Screens
Every platform expects app icons and launch/splash screens in a whole range of specific pixel sizes — manually creating and placing each one is tedious and error-prone. Two widely used packages handle this from a single source image instead:
flutter_launcher_icons— generates every required app icon size for Android and iOS from one source PNG.flutter_native_splash— generates a native splash screen (the screen shown instantly while Flutter itself finishes loading) for every platform from one configuration.
Both run as a one-time command against your project after a short YAML configuration, saving significant manual image work compared to producing every required size by hand.