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.

Critical warning: if you lose your keystore, you permanently lose the ability to publish updates to that same app listing on the Play Store — Google cannot recover it or issue you a new one for an existing listing. Back up your keystore file and its passwords somewhere safe and durable (a password manager and an encrypted backup, not just your laptop) the moment you create it.

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
PartMeaning
1.0.0The semantic version shown to users on the store listing (major.minor.patch)
+1The 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.

Next up: head to Performance Tips to make sure the app you just learned to build actually runs smoothly for every user.