Build & Publish to the Stores
Running your app with flutter run is not the same as shipping it. A release build must have a permanent app ID, a version number, an icon, a digital signature that proves it comes from you, and it must pass the store's review. This lesson is an overview of the whole path to the Google Play Store and the Apple App Store, with the commands you will use.
Store rules, fees and forms change often, so treat this lesson as a map and always follow the current official guides: the Flutter deployment pages on docs.flutter.dev, the Play Console Help Centre and Apple's App Store Connect help.
Before any release
Go through this checklist for both platforms:
- App ID. Android's
applicationId(inandroid/app/build.gradle.kts) and iOS's bundle identifier, such asin.meera.expense_tracker. Once the app is on a store, the ID can never change. - App name. The name under the icon:
android:labelinAndroidManifest.xmland the display name in Xcode (orCFBundleDisplayNameinInfo.plist). - Version.
version: 1.2.0+5inpubspec.yaml. The part before+is the version users see; the build number after+must increase with every upload. You can override both:flutter build appbundle --build-name=1.2.0 --build-number=5. - Icon. Replace the default Flutter icon. The
flutter_launcher_iconspackage generates every size from one image. - Permissions. Only what you use, with clear iOS usage descriptions (see Platform Permissions), and
INTERNETin the main Android manifest. - Tests and analysis.
flutter analyzewith no issues andflutter testpassing. - Privacy policy. Both stores ask for a privacy policy URL and a description of the data you collect.
Then test a release build on a real phone: flutter run --release. Release builds are compiled ahead of time and behave slightly differently from debug builds, so problems such as a missing permission only appear there.
Android: signing with an upload key
Google Play needs your app to be signed. You create an upload key once and keep it safe forever. With Play App Signing, Google holds the final app-signing key, and your upload key proves that new uploads come from you. On Windows (PowerShell):
keytool -genkey -v -keystore $env:USERPROFILE\upload-keystore.jks `
-storetype JKS -keyalg RSA -keysize 2048 -validity 10000 `
-alias uploadkeytool is part of the Java Development Kit (JDK); Android Studio includes one, and flutter doctor -v shows which Java installation Flutter uses. The command asks for passwords and your name or organisation. Then create android/key.properties:
storePassword=your-store-password
keyPassword=your-key-password
keyAlias=upload
storeFile=C:\\Users\\meera\\upload-keystore.jksOn Windows, write the path with double backslashes as shown. Finally, edit android/app/build.gradle.kts so the release build uses this key:
import java.io.FileInputStream
import java.util.Properties
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
// namespace, compileSdk, defaultConfig ... as generated
signingConfigs {
create("release") {
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
storeFile = keystoreProperties.getProperty("storeFile")?.let { file(it) }
storePassword = keystoreProperties.getProperty("storePassword")
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
}The generated file signs release builds with the debug key so that flutter run --release works out of the box; this change replaces that line. Keep the plugins { } block at the top of the file as generated.
Android: build an App Bundle and upload
flutter build appbundle
flutter build apk --split-per-abiflutter build appbundle creates an Android App Bundle (.aab) in build/app/outputs/bundle/release/. Google Play uses the bundle to generate optimised APKs for each device, so users download less. New apps on Google Play must be published as App Bundles. flutter build apk --split-per-abi creates separate APK files for each processor type in build/app/outputs/flutter-apk/; APKs are useful for sharing test builds directly or for stores that do not accept bundles.
Then, in the Google Play Console:
- Create a developer account (there is a one-time registration fee; check the current amount in the Play Console) and complete identity verification.
- Create the app, fill in the store listing: title, short and full description, screenshots, feature graphic, category and contact details.
- Complete the App content forms: privacy policy, data safety (what data you collect and why), content rating questionnaire, target audience and ads declaration.
- Upload the
.aabto a testing track first (internal, then closed testing), install it from the Play Store on test phones, and fix problems. - Promote the release to Production and submit it for review. Review usually takes from hours to several days.
Google Play also requires apps to target a recent Android API level; Flutter's template sets targetSdk = flutter.targetSdkVersion, which current Flutter versions keep up to date. New personal developer accounts may have to run a closed test with a minimum number of testers for a set period before they can publish to production; the Play Console shows the current requirement.
iOS: build, TestFlight and App Store review
iOS releases need a Mac with Xcode and membership of the Apple Developer Program (an annual fee; see Apple's website for the current price in India).
- Open
ios/Runner.xcworkspacein Xcode. Under Signing & Capabilities, choose your team and set the bundle identifier. Xcode can manage certificates and provisioning profiles automatically. - In App Store Connect, create a new app record with the same bundle identifier, name, primary language and SKU.
- Build the release archive from the terminal:
flutter build ipaThis creates an Xcode archive and an .ipa file under build/ios/. Upload the .ipa with Apple's Transporter app, or open the archive in Xcode's Organizer and click Distribute App.
- The build appears in App Store Connect after processing. Add internal or external testers in TestFlight to test it on real iPhones.
- Fill in the App Store listing (screenshots for the required device sizes, description, keywords, privacy "nutrition label" and age rating), choose the build and Submit for Review.
Apple's App Review checks the app against its guidelines. Common rejection reasons are crashes, broken links, placeholder content, missing login details for the reviewer, and vague permission descriptions. Reply in App Store Connect, fix the issue and resubmit.
Shrinking and protecting the release
flutter build appbundle --obfuscate --split-debug-info=build/symbolsmakes the Dart code harder to reverse-engineer. Keep thebuild/symbolsfolder for that version; you need it to read crash stack traces.flutter build apk --analyze-size(and the same flag for other targets) shows what takes space in your app.- Remember: obfuscation does not make secrets safe. Never ship API secrets in the app.
After launch, watch the crash and ANR (app not responding) reports in Play Console and the crash reports in Xcode Organizer or App Store Connect, answer user reviews, and ship updates with a higher build number.
Common mistakes
- Uploading with the same build number. Both stores reject a build number they have already seen.
- Committing the keystore or
key.propertiesto a public repository. - Testing only debug builds. Always install and try the release build before submitting.
- Changing the app ID after publishing. The store treats it as a completely different app.
- Incomplete store forms. Missing privacy policy or data safety answers block the release.
Key points
- Set a permanent app ID, app name, version (
name+build), icon and minimal permissions before releasing. - Android: create an upload keystore, reference it from
key.propertiesandbuild.gradle.kts, and build withflutter build appbundle. - Google Play Console: listing, app content forms, testing tracks, then production review.
- iOS: Apple Developer Program, Xcode signing,
flutter build ipa, upload, TestFlight, then App Review. - Increase the build number for every upload, keep keys secret and backed up, and test release builds on real devices.
Exercise
Your expense tracker is at version: 1.0.0+1 and already live on Google Play. You fixed a bug and added a new chart screen. Decide the new version, write the pubspec.yaml line, and write the commands to (a) build a signed App Bundle with obfuscation, and (b) build an iOS archive with the same version. Then list three things you would check before submitting.
Show solution
version: 1.1.0+2flutter test
flutter build appbundle --obfuscate --split-debug-info=build/symbols
flutter build ipa --obfuscate --split-debug-info=build/symbolsA new feature without breaking changes is a minor release, so the version becomes 1.1.0, and the build number must go up to 2. Before submitting, check that the release build installs and runs on a real phone, that the store screenshots and "What's new" text mention the chart screen, and that build/symbols is saved for reading crash reports of this version.
Quick Quiz
1. What does version: 2.3.1+14 in pubspec.yaml mean?
The part after + is the build number, which must increase with each upload.
2. Which file format do new apps upload to Google Play?
flutter build appbundle creates the .aab that Play uses to make device-specific APKs.
3. Why must the upload keystore never be committed to a public repository?
The signing key proves an upload comes from you, so it must stay private and backed up.
4. What do you need to publish an iOS app?
iOS builds need Xcode, and publishing needs Apple Developer Program membership.
Learn Flutter & Dart with a mentor
Free lessons get you started. Our Mobile App Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
