DEV Community

Play Console Store
Play Console Store

Posted on

Publish Flutter App to Google Play: The End-to-End Release Guide

Flutter makes building the app the easy part. Shipping it to Google Play — release signing, Gradle configuration, the AAB build, the store listing — is where Flutter developers lose the most time.

Publishing a Flutter app to Google Play is a checklist discipline from signing to release.

This guide walks through publishing a Flutter app to Google Play end to end: signing configuration, build.gradle settings, the AAB build command, store listing specifics, and the build failures you'll actually hit.

Prerequisites

Before touching Gradle, make sure you have:

  • A Google Play Console developer account with an app created (note the package name — it must match your Flutter applicationId and can never change)
  • Your app's icons, feature graphic, and screenshots ready
  • Flutter stable channel, and a successful flutter build apk --release at least once (proves release mode compiles)

Step 1: Release Signing Configuration

This is the step that generates the most Stack Overflow questions. Android requires release builds to be signed, and Flutter doesn't do it for you.

Release signing configuration is the step that generates the most questions for Flutter apps.

Generate the upload keystore (do this once per app):

keytool -genkey -v -keystore ~/upload-keystore.jks \
    -keyalg RSA -keysize 2048 -validity 10000 \
    -alias upload
Enter fullscreen mode Exit fullscreen mode

Answer the prompts and remember the passwords. Back up this file immediately — in two places. Losing it means a key-reset process with Google and delayed releases.

Reference it securely. Create android/key.properties (and add it to .gitignore — never commit keystores or passwords):

storePassword=YOUR_STORE_PASSWORD
keyPassword=YOUR_KEY_PASSWORD
keyAlias=upload
storeFile=/home/you/upload-keystore.jks
Enter fullscreen mode Exit fullscreen mode

Wire it into android/app/build.gradle:

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    // ...
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The if (exists()) guard matters: it lets debug builds and CI environments without the keystore still configure correctly.

Step 2: app/build.gradle Settings for Release

Beyond signing, review these settings in android/app/build.gradle:

android {
    namespace "com.example.myapp"
    compileSdk 35

    defaultConfig {
        applicationId "com.example.myapp"  // must match Play Console, immutable
        minSdk 23                          // Flutter's floor; raise if you need newer APIs
        targetSdk 35                       // keep current per Play requirements
        versionCode 7                      // integer, increment every release
        versionName "1.2.0"                // user-visible
        multiDexEnabled true               // needed once you cross the 64K method limit
    }
}
Enter fullscreen mode Exit fullscreen mode

Also check android/build.gradle for the Kotlin version — Flutter pins one, but plugin updates sometimes require bumping ext.kotlin_version. Mismatched Kotlin versions are a top cause of mysterious release-build failures.

Shrink and obfuscate (recommended for release):

buildTypes {
    release {
        signingConfig signingConfigs.release
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
Enter fullscreen mode Exit fullscreen mode

If you enable R8/ProGuard, keep Flutter's required rules — the Flutter tool generates the basics, but plugins using reflection need explicit -keep rules. Test the release bundle on a device; reflection breakage only shows at runtime.

Step 3: Build the AAB

Google Play requires the Android App Bundle format for new apps. Build it with:

flutter build appbundle --release
Enter fullscreen mode Exit fullscreen mode

Output: build/app/outputs/bundle/release/app-release.aab.

For a versioned, CI-friendly build:

flutter build appbundle --release \
    --build-number=8 \
    --build-name=1.2.1
Enter fullscreen mode Exit fullscreen mode

--build-number sets versionCode; --build-name sets versionName. Driving these from CI environment variables is the reliable pattern:

flutter build appbundle --release \
    --build-number=$CI_BUILD_NUMBER \
    --build-name=1.2.1
Enter fullscreen mode Exit fullscreen mode

Upload the .aab to Play Console → your app → Testing → Internal testing (first), then promote through closed → open → production. Always soak a release on the internal track first — Play's pre-launch report will catch crashes on real devices you don't own.

Step 4: Store Listing Specifics for Flutter Apps

Flutter apps have a few listing considerations worth getting right:

  • Screenshots must be real. Capture from the actual release build on real devices — Flutter's debug banner or hot-reload artifacts in screenshots look amateur and can draw reviewer attention.
  • Feature graphic (1024×500) is required. Flutter's default launcher icon is recognizable; replace it before publishing — shipping the default Flutter icon is the fastest way to look unfinished.
  • Data safety form: audit your plugins. Firebase Analytics, crashlytics, ad plugins — even shared_preferences can count as data collection. Match your privacy policy to actual SDK behavior; mismatches are a leading rejection trigger for Flutter apps, because developers forget what their plugins do.
  • Permissions audit: run a release build and inspect the merged manifest — Flutter plugins are notorious for merging in permissions you never declared (ACCESS_FINE_LOCATION from a maps plugin you only use for static display, for example). Remove or justify each one.
  • App access credentials: if your app has login, provide demo credentials in Play Console (Policy → App content → App access). Reviewers can't test a login-walled Flutter app without them, and "needs credentials" rejections are embarrassingly common.

Step 5: Common Build Failures and Fixes

"Execution failed for task ':app:validateSigningRelease'"
The signing config isn't resolving — usually key.properties is missing or the path is wrong. Check the file exists at android/key.properties and the storeFile path is absolute and correct.

Kotlin version conflicts
Error mentions kotlin-stdlib version mismatches between plugins. Align ext.kotlin_version in android/build.gradle with what your newest plugin requires, then flutter clean and rebuild.

"Manifest merger failed" / duplicate permissions
Two plugins declaring conflicting manifest entries is common. Run with --info to see the merger report, then add tools:replace or tools:remove in android/app/src/main/AndroidManifest.xml.

Release build crashes that debug doesn't
Almost always one of: ProGuard stripping reflection-used classes (add -keep rules), a plugin initializing differently in release, or missing network security config. Reproduce with flutter run --release before rebuilding the bundle — it's much faster than the AAB round-trip.

64K method limit (MultiDex)
If you see Cannot fit requested classes in a single dex file, set multiDexEnabled true and ensure minSdk is 21+ (native multidex) or add the multidex dependency for lower floors.

"Your app currently targets API XX" upload rejection
Bump targetSdk in app/build.gradle, then actually test on that API level — Flutter apps hitting new Android behavior changes (notification permissions on 13+, photo picker on 14) break in subtle ways.

Release-Day Checklist

  • [ ] flutter build appbundle --release succeeds cleanly
  • [ ] Bundle tested via internal track; pre-launch report reviewed
  • [ ] versionCode incremented; versionName correct
  • [ ] Signing keystore backed up (two locations)
  • [ ] Merged manifest audited for plugin-added permissions
  • [ ] Data safety form matches actual SDK behavior
  • [ ] Demo credentials provided if login-gated
  • [ ] Screenshots from release build; custom icon, no debug banner

The release-day checklist for publishing a Flutter app to Google Play.

Publishing a Flutter app to Google Play is a checklist discipline. Get signing right once, automate version codes in CI, audit plugin manifests and data collection every release, soak everything on the internal track. Do that and releases become uneventful — which is the goal.

If you'd rather hand the publishing pipeline to someone who's done it dozens of times — signing setup, AAB builds, store listing, policy review — Play Console Store is an independent marketplace where Android specialists offer end-to-end publishing support for Flutter and native apps alike.


The author is an Android and Flutter developer who helps teams ship to Google Play. He writes about Flutter releases, app signing, and Play policy at Play Console Store — an independent marketplace, not affiliated with or endorsed by Google LLC.

Top comments (0)