Flutter Lesson 77 of 83 5 min read
Build and Release a Flutter App for Android
Build a signed Android app bundle with Flutter and publish it on Google Play: keystore, signing config, versioning and store listing.
On this page
Publishing on Google Play has four stages: sign the app, build it, create a store listing, and upload. Store requirements change, so check the Play Console’s own guidance as you go.
Before you build #
- App icon, name and app id are set. See the previous lesson.
- The Internet permission is in
AndroidManifest.xmlif the app uses the network. Debug builds add it automatically; release builds do not. -
versioninpubspec.yamlis correct, with a build number higher than any earlier upload. - The app works in release mode:
flutter run --releaseon a real device. - No test data, debug menus or
printstatements with private information remain.
1. Create an upload keystore #
Android requires every app to be signed. Create a keystore once and keep it safe.
On macOS or Linux:
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA \
-keysize 2048 -validity 10000 -alias upload
On Windows (PowerShell):
keytool -genkey -v -keystore $env:USERPROFILE\upload-keystore.jks `
-storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias upload
You will be asked for a password and some details.
Back up the keystore file and its passwords, somewhere other than your laptop. Never commit them to Git.
2. Tell Gradle about the keystore #
Create android/key.properties:
storePassword=your-store-password
keyPassword=your-key-password
keyAlias=upload
storeFile=/Users/you/upload-keystore.jks
Add it to android/.gitignore:
key.properties
*.jks
Then edit android/app/build.gradle.kts. Load the properties near the top of the file:
import java.util.Properties
import java.io.FileInputStream
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
And inside the android { } block, add a signing configuration and use it for release builds:
android {
// ...
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String?
keyPassword = keystoreProperties["keyPassword"] as String?
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
storePassword = keystoreProperties["storePassword"] as String?
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
}
Older projects have a build.gradle file written in Groovy, with slightly different syntax. The Flutter documentation on Android deployment shows both forms.
3. Check the app configuration #
In the same Gradle file:
defaultConfig {
applicationId = "com.yourcompany.myshop" // your permanent id
minSdk = flutter.minSdkVersion // lowest Android version supported
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode // from pubspec.yaml
versionName = flutter.versionName
}
Google Play requires a recent targetSdk for new apps and updates. Keeping Flutter up to date usually takes care of it.
4. Build #
Google Play wants an app bundle (.aab). It lets Play deliver only the code and resources each device needs, so downloads are smaller.
flutter build appbundle
The result is at build/app/outputs/bundle/release/app-release.aab.
To install directly on a device, or to distribute outside Play, build APKs:
flutter build apk --split-per-abi
This produces one smaller APK per processor type, in build/app/outputs/flutter-apk/.
Useful options:
flutter build appbundle --build-name=1.2.0 --build-number=7
flutter build appbundle --obfuscate --split-debug-info=build/symbols
flutter build appbundle --dart-define=ENV=prod
--obfuscate makes the compiled Dart harder to read. Keep the symbols folder: you need it to make sense of crash reports from that build.
5. Test the release build #
flutter install --release
Check in particular: network requests, anything that reads files or assets, and every plugin. Problems that appear only in release usually come from a missing permission or from code shrinking removing something that a plugin needs.
6. Create the Play Console listing #
You need a Google Play developer account, which has a one-time registration fee. Then, in the Play Console:
- Create app: name, default language, app or game, free or paid.
- Store listing: short description (80 characters), full description, app icon (512 by 512), feature graphic (1024 by 500), and screenshots for phones, plus tablets if you support them.
- App content: privacy policy URL, data safety form, content rating questionnaire, target audience, and whether the app has ads.
- Pricing and countries.
The data safety form asks what data the app collects and why. Answer accurately, including data collected by the packages you use, such as analytics and crash reporting.
7. Upload and roll out #
Play has testing tracks. Use them.
| Track | Audience | Use |
|---|---|---|
| Internal testing | Up to 100 testers you name | Quick checks. Available within minutes |
| Closed testing | Invited groups | Wider testing before launch |
| Open testing | Anyone who opts in | A public beta |
| Production | Everyone | The real release |
New personal developer accounts may be required to run a closed test with a number of testers for a period before they can publish to production. The Play Console tells you what applies to your account.
Upload the .aab to a track, add release notes, and submit for review. Review takes from hours to several days.
When you first upload, enrol in Play App Signing. Google then holds the final signing key, and your keystore is only an upload key, which can be reset if you lose it.
Updates #
- Raise the version in
pubspec.yaml:1.2.1+8. The build number must increase. - Build the bundle.
- Create a new release in the Play Console and upload.
- Consider a staged rollout: release to 10 percent of users first, watch for crashes, then widen.
Common problems #
| Problem | Likely cause |
|---|---|
| No data in release, works in debug | Missing Internet permission |
| “Version code already used” | Increase the number after + |
| “Signed in debug mode” | The release signing config is not applied |
| Crashes only in release | Code shrinking removed a class. Check the plugin’s ProGuard rules |
| Rejected by review | Privacy policy, permissions or data safety answers. The email says which |
Try it yourself #
Create a keystore and configure signing for one of your apps. Build an app bundle and a set of split APKs, install the release APK on a phone, and compare its size and start-up speed with the debug build.