FLUTTER: Publishing Flutter Apps

Publishing a Flutter app to the App Store and Google Play requires careful preparation — versioning, app icons, signing, permissions, obfuscation, testing, and store submission. This covers the complete release process for both iOS and Android.

Building a Flutter app is only half the journey. Getting it into users' hands requires navigating the platform-specific release processes for both the Apple App Store and the Google Play Store. Doing it right from the start saves significant time and avoids repeated rejections.


Pre-Publication Checklist

Before building a release, ensure the following are in order:

  • Correct app version and build number in pubspec.yaml
  • App icon and splash screen configured
  • Platform permissions declared
  • App signed with the appropriate certificate
  • Code obfuscation configured
  • All tests passing
  • Release build tested on a real device

Versioning

Flutter version numbers live in pubspec.yaml.

yaml
name: my_app
description: A great Flutter app
version: 1.2.3+45

The format is major.minor.patch+build:

PartDescriptionExample
majorBreaking changes1
minorNew features, backward compatible2
patchBug fixes3
buildInternal build number (integer, increments each release)45

The build number must be higher than the previously uploaded build on every new submission to the stores.


App Icons and Splash Screens

App Icons

Use the flutter_launcher_icons package to generate icons for all required sizes automatically.

bash
flutter pub add dev:flutter_launcher_icons

Add configuration to pubspec.yaml:

yaml
flutter_launcher_icons:
  android: true
  ios: true
  image_path: "assets/icon/icon.png"
  min_sdk_android: 21
  web:
    generate: true
    image_path: "assets/icon/icon.png"

Generate the icons:

bash
flutter pub run flutter_launcher_icons:main

Splash Screens

Use flutter_native_splash to configure native splash screens that appear before Flutter loads.

bash
flutter pub add dev:flutter_native_splash

yaml
flutter_native_splash:
  color: "#ffffff"
  image: assets/splash.png
  android_12:
    image: assets/splash.png
    color: "#ffffff"

bash
flutter pub run flutter_native_splash:create


App Permissions

Permissions inform users what device capabilities the app requires and are reviewed during store submission.

Android

Declare permissions in android/app/src/main/AndroidManifest.xml:

xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    ...
</manifest>

iOS

Add usage descriptions to ios/Runner/Info.plist:

xml
<key>NSCameraUsageDescription</key>
<string>This app uses the camera to scan QR codes.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses location to show nearby stores.</string>

Always provide a clear, honest description — vague descriptions are a common rejection reason on iOS.


Signing the App

Signing proves that the app comes from a trusted source and has not been tampered with.

Android Signing

Step 1 — Generate a keystore:

bash
keytool -genkey -v -keystore ~/my-release-key.jks \
  -keyalg RSA -keysize 2048 -validity 10000 \
  -alias my-key-alias

Step 2 — Reference the keystore in android/key.properties:

text
storePassword=your_store_password
keyPassword=your_key_password
keyAlias=my-key-alias
storeFile=/path/to/my-release-key.jks

Step 3 — Configure android/app/build.gradle:

gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

android {
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

iOS Signing

iOS signing is managed by Xcode and the Apple Developer portal.

md
iOS Signing Process
-------------------
1. Generate a Certificate Signing Request (CSR)
   (Keychain Access --> Certificate Assistant --> Request a Certificate)
      |
2. Create Development and Distribution Certificates
   (developer.apple.com --> Certificates, Identifiers & Profiles)
      |
3. Create an App Identifier (Bundle ID)
      |
4. Create a Provisioning Profile
   (links the App ID, certificates, and registered devices)
      |
5. Install the profile in Xcode


Code Obfuscation

Obfuscation makes your compiled code significantly harder to reverse-engineer.

Android

bash
flutter build apk --release --obfuscate --split-debug-info=./debug-info/android

iOS

bash
flutter build ios --release --obfuscate --split-debug-info=./debug-info/ios

Store the debug info directory securely — it is required to decode any crash stack traces from obfuscated builds.


Testing Before Release

Unit Tests

bash
flutter test

Integration Tests

Use Firebase Test Lab or run on real devices:

bash
flutter drive --target=test_driver/app.dart

Always test the release build (not debug) on real devices for both iOS and Android before submitting. Release builds behave differently — they are AOT-compiled and obfuscated, which can surface issues that do not appear in debug builds.


Building Release Packages

Android

Build a signed APK:

bash
flutter build apk --release

Build a signed App Bundle (preferred by Google Play):

bash
flutter build appbundle --release

AAB vs APK:

FormatDescription
AAB (App Bundle)Google Play generates optimized APKs per device. Preferred format. Smaller download sizes.
APKTraditional package. Required for direct distribution outside Google Play.

iOS

bash
flutter build ios --release

Then archive and upload via Xcode's Organizer.


Submitting to the App Stores

Google Play (Android)

  1. Go to Google Play Console
  2. Create an application record
  3. Complete all store listing metadata (title, description, screenshots, privacy policy)
  4. Upload the .aab file in the Production track
  5. Submit for review

The Google Play Console also provides:

  • Pre-launch testing with Firebase Test Lab
  • In-app purchases management
  • Performance monitoring and crash reporting
  • Analytics and user behavior tracking via Google Analytics

Apple App Store (iOS)

  1. Log in to App Store Connect
  2. Create a new App Record (requires a registered Bundle ID)
  3. Fill in metadata: name, subtitle, description, keywords, screenshots
  4. Archive the app in Xcode and upload via the Organizer
  5. Select the build in App Store Connect and submit for review

Integrating Analytics

Firebase Analytics for Android

bash
flutter pub add firebase_analytics

Configure in pubspec.yaml and follow the Firebase setup steps. Firebase Analytics provides:

  • User demographics (location, age, interests)
  • Behavior flows (screen visits and navigation paths)
  • Real-time active user counts
  • Conversion tracking (sign-ups, purchases, custom events)

dart
import 'package:firebase_analytics/firebase_analytics.dart';

final analytics = FirebaseAnalytics.instance;

// Log a custom event
await analytics.logEvent(
  name: 'purchase_complete',
  parameters: {'item_id': 'sku_001', 'value': 9.99},
);


Release Summary

md
Prepare
  --> Update version in pubspec.yaml
  --> Configure icons and splash screens
  --> Declare platform permissions

Build
  --> Sign (keystore for Android, certificates for iOS)
  --> Obfuscate (--obfuscate --split-debug-info)
  --> Build release package (appbundle / ios)

Test
  --> Run all tests
  --> Test release build on real devices

Submit
  --> Google Play Console (Android)
  --> App Store Connect (iOS)

Publishing is not a one-time event. Each update follows the same cycle — version bump, build, test, and submit. Automating this with CI/CD (GitHub Actions, Fastlane, Codemagic) saves significant time once your app is in active development.