Flutter builds your app. Getting it onto Google Play is up to you: you sign and build an Android App Bundle, then publish it through Play Console. This guide covers the whole route in 8 steps, updated for Google Play's 2026 rules: target API 36, 16 KB page sizes and the 12-tester closed test.
Shortcut Rather not read it all? Copy our AI prompt and your AI assistant walks you through every step.
Get ready
Before you start
Check your Flutter version
Google Play's rules decide which Flutter version you need. Since 31 August 2026, new apps and updates must target Android 16 (API level 36), and Flutter 3.35 is the first version that does this by default.
| Flutter version | What it gives you |
|---|---|
| 3.29 or newer | New projects use build.gradle.kts (Kotlin) instead of build.gradle. |
| 3.35 or newer | Targets API 36 by default, as Google Play requires. Raises the minimum to Android 7 (API 24). |
| 3.38 or newer | Uses NDK r28 by default. Flutter recommends it for 16 KB page sizes. |
| 3.47.7 | The current stable version, released 8 October 2026. |
flutter --version
flutter upgrade
Have these ready
- Flutter 3.35 or newerRun
flutter upgradeif you are on an older version. - Android StudioIt installs the Android SDK and the Java tools you use to make your key.
- A Google Play developer accountUS$25, paid once. Step 5 covers it.
- A privacy policy pageAt a public web address that opens without signing in.
- Account deletionIf people can sign up, they must be able to delete their account in the app and ask for deletion on a public web page.
- 12 or more testersNew personal Play accounts only. Step 7 explains why.
Built with FlutterFlow?
FlutterFlow's Basic plan (US$39 a month) can upload to Google Play for you, under Settings & Integrations › App Settings › Mobile Deployment, using a Google Cloud service account. You still upload your first bundle by hand. Put it on the Closed testing track, because internal testing does not count toward the 12 testers. FlutterFlow's own docs suggest Internal testing; if its track menu does not offer Closed testing, upload your test builds by hand. Or download the code and follow this guide.
1. Get your project ready
A few settings in your project decide how your app appears on Google Play. Some of them can never change after your first upload, so set them now.
Where pubspec.yaml, AndroidManifest.xml, build.gradle.kts
- Target API 36. On Flutter 3.35 or newer, keep
targetSdk = flutter.targetSdkVersioninandroid/app/build.gradle.kts. If the file has a number there instead, setcompileSdkandtargetSdkto 36. If the build then asks for a newer Android Gradle Plugin, raise it inandroid/settings.gradle.kts(8.9.1 or newer) and Gradle inandroid/gradle/wrapper/gradle-wrapper.properties(8.11.1 or newer). - App name. Set
android:labelinandroid/app/src/main/AndroidManifest.xml. - App ID. In
build.gradle.kts, changeapplicationIdfromcom.example.your_appto your own, such ascom.yourname.habittracker. Google Play does not acceptcom.example, and the ID can never change after your first upload. Changenamespaceand the package line ofMainActivity.ktto match, and move that file to the matching folder. Using Firebase? Add the new package name in the Firebase console (or runflutterfire configureagain) and download a newgoogle-services.json, or the build fails with "No matching client found for package name". - Internet. If your app goes online, add the INTERNET permission to
src/main/AndroidManifest.xml. New projects only have it in the debug and profile manifests, so release builds cannot reach the internet. - Icon. Add your launcher icon, for example with the
flutter_launcher_iconspackage. - Version. In
pubspec.yaml,version: 1.0.0+1means version name 1.0.0 and build number 1. The build number must go up with every upload.
<uses-permission android:name="android.permission.INTERNET"/>
You should see Your app working on your phone with flutter run --release, internet and all.
2. Create your upload key
Every bundle you upload must be signed with the same key, called your upload key. You make it once and keep it safe.
Where Terminal
- Find
keytool, which comes with Android Studio. Runflutter doctor -v, find the line Java binary at:, and usekeytoolfrom the same folder. On Windows, if that path has spaces (such asProgram Files), put it in quotes and start the line with&:& "C:\Program Files\...\keytool.exe"followed by the rest of the command. - Run the command below. Choose a password, answer the questions, and keep the file outside your project folder.
- Create
android/key.propertieswith your passwords and the path to the file. - Check that
android/.gitignorelistskey.propertiesand*.jks(new projects already do), and back both files up somewhere safe.
macOS or Linux:
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA \
-storetype JKS -keysize 2048 -validity 10000 -alias upload
Windows (PowerShell):
keytool -genkey -v -keystore $env:USERPROFILE\upload-keystore.jks `
-storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias upload
android/key.properties (on Windows, write the path with double backslashes):
storePassword=<your keystore password>
keyPassword=<your key password>
keyAlias=upload
storeFile=<full path to upload-keystore.jks>
Replace each <...>, including the angle brackets.
android folder in Android Studio, choose Build › Generate Signed App Bundle or APK, pick Android App Bundle, click Next, then Create new under Key store path. Image: Android Developers.What if you lose the key later?
You are not locked out. Google Play keeps its own signing key and can reset your upload key: make a new key, export its certificate with the command below, then use Protected with Play › Play Store protection › Manage Play app signing › Request upload key reset and upload the .pem file. You need admin permission on the account. Backing up is still much quicker.
keytool -export -rfc -keystore upload-keystore.jks -alias upload -file upload_certificate.pem
You should see An upload-keystore.jks file and an android/key.properties file that Git ignores.
3. Sign your release builds
New Flutter projects sign release builds with the debug key, and Google Play refuses those. This tells release builds to use your upload key instead.
Where android/app/build.gradle.kts
- Open
android/app/build.gradle.kts. - Add the two imports and the lines that load
key.propertiesabove theandroidblock. - Add the
signingConfigsblock, and changesigningConfigin the release build type fromgetByName("debug")togetByName("release"). - Run
flutter clean.
Add the new lines to your file; do not paste this over it. The ... parts stand for what is already there. You can delete the old TODO comment lines above signingConfig.
import java.util.Properties
import java.io.FileInputStream
plugins { ... }
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
// ...
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")
}
}
}
Older project with build.gradle?
Projects made before Flutter 3.29 may have android/app/build.gradle (Groovy) instead. The Kotlin lines above break a Groovy file, so use Flutter's Groovy version, from the release guide:
import java.util.Properties
import java.io.FileInputStream
plugins { ... }
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
}
}
}
You should see Release builds signed with your upload key. Keep key.properties out of Git.
4. Build the app bundle
Google Play needs an Android App Bundle, an .aab file. One command builds it.
Where Terminal, in your project folder
- Run
flutter build appbundle. It builds a release version by default. - Find your file at
build/app/outputs/bundle/release/app-release.aab. - Optional: make your Dart code harder to read with
--obfuscate --split-debug-info. Back up thesymbolsfolder for every release, or you cannot read crash reports later. - Check 16 KB support if your app uses plugins with native code. Build an APK with
flutter build apk, open it with Build › Analyze APK in Android Studio, and look at the Alignment column.
flutter build appbundle
With obfuscation:
flutter build appbundle --obfuscate --split-debug-info=symbols
What 16 KB means for you
Some newer Android devices use 16 KB memory pages. Every app with native code, which includes every Flutter app, must support them on Google Play. A new app must support them now; 1 February 2027 is only the extended deadline for updates to existing apps. Problems usually come from older plugins. Update Flutter to 3.38 or newer and update your plugins.
You should see app-release.aab in build/app/outputs/bundle/release/.
5. Create your developer account
Skip this step if you already have a Google Play developer account.
Where play.google.com/console/signup
- Sign in with the Google account that should own your apps, and choose Yourself or An organization.
- Pay the US$25 fee with a credit or debit card.
- Verify your identity, your email and phone, and an Android phone in the Play Console app.
Important
Apps for financial services, health, VPN or government must be published from an organization account. Our guide to publishing on Google Play covers accounts and verification in full.
6. Create the app in Play Console
Create the app with your exact application ID, then fill in the store listing and the App content questions.
Where Play Console › Home › Create app
- Click Create app. Enter the app name and your
applicationIdas the package name, then click Check availability. - Choose the language, App or Game, and Free or Paid. A free app can never become paid later.
- Go to Grow users › Store presence › Store listings: an app name of up to 30 characters, a short description of up to 80, a full description of up to 4,000, a 512 by 512 icon, a 1024 by 500 feature graphic and 2 to 8 phone screenshots.
- Go to Policy and programs › App content and answer every item. Data safety must cover every plugin that collects data, such as Firebase, analytics and ads.
applicationId exactly as it is in build.gradle.kts.
Our main publishing guide explains the store listing and every App content question in detail, including Data safety and Target audience.
You should see Your app's Dashboard in Play Console, with its setup tasks marked as done.
7. Upload the bundle and run your closed test
Personal accounts created after 13 November 2023 need at least 12 testers who stay opted in for 14 days in a row before the app can go public.
Where Test and release › Testing › Closed testing
- Open Closed testing and click Manage track on the track that is already there (extra closed tracks you create cannot target countries). Add all countries, or at least every country your testers live in.
- Add your testers on the Testers tab, with an email list or a Google Group.
- Create a new release and upload
app-release.aab. - Send the release for review from Publishing overview. This usually takes a day or two.
- Copy the opt-in link from the Testers tab and send it to every tester. Keep them using the app for 14 days.
Short of testers? Our guide to getting 12 testers for Google Play covers every way to find them.
Important: internal testing does not count
Internal testing is fine for a quick check, but only a closed test counts toward the 12 testers and 14 days. You can upload a fixed build to the same closed track during the test. Testers stay opted in, so their 14 days keep counting.
Using Google sign-in or Firebase?
Sign-in that works with flutter run can fail in the version from Google Play, because Google signs that version with its own key. After your first upload, open Protected with Play › Play Store distribution › Go to Play app signing, copy every SHA-1 and SHA-256 fingerprint in the App signing key section (new apps can list three keys), add them all in Firebase, and download a fresh google-services.json.
No 12 testers? We run this step for you.
Starter $15
Testing starts within 6 hours: 15 verified testers join your closed test and stay for the full 14 days. Anyone inactive is replaced. Comes with our production access guarantee, or a full refund.
Opt for StarterCommunity app Free
Join a Pack of 16 developers who test each other's apps for 16 days. Inactive members are replaced.
Get the free app8. Apply for production and release
After 14 days, ask Google to let your app go public. Then release it.
Where Dashboard › Apply for production, then Test and release › Production
- On the Dashboard, click Apply for production and answer the form's 10 questions (2 are multiple choice) about your test and your app. Be specific about what testers told you and what you changed.
- Google usually replies by email within 2 to 3 days.
- Once approved, open Production and click Create new release.
- Click Add from library to reuse your tested bundle, or upload a newer one, then write release notes and choose countries.
- Send the release for review from Publishing overview. The review usually takes 2 to 3 days, and can take up to 7.
Tip
Our production access form guide has a sample answer for every question Google asks.
You should see The release In review, then your Flutter app live on Google Play.
Your Flutter app is on Google Play
Its public link is play.google.com/store/apps/details?id= followed by your application ID.
Next
Updating your app after launch
Every update is a new bundle with a higher build number. Change version: 1.0.0+1 to
1.0.1+2 in pubspec.yaml, run flutter build appbundle, then create a new
release in Play Console, upload the bundle and send it for review.
version: 1.0.1+2
Code push tools can patch Dart code without a store update. Google Play's policy makes an exception for code that runs in a virtual machine or an interpreter, but the patched app must still follow every Play policy. Native code and new plugins always need a new release.
If it goes wrong
Common Flutter errors on Google Play
These are the problems Flutter developers run into most often, with the fix for each:
| What you see | How to fix it |
|---|---|
| "You uploaded an APK or Android App Bundle that was signed in debug mode" | Release builds still use the debug key. Do step 3, run flutter clean and build again. |
| "Version code 1 has already been used" | Raise the number after the + in pubspec.yaml above every build you ever uploaded, on any track. |
| The app works in debug but has no internet from Google Play | Add the INTERNET permission to src/main/AndroidManifest.xml (step 1). |
| "com.example" is restricted | Change applicationId before your first upload (step 1). |
| "App must target Android 16 (API level 36) or higher" | Upgrade to Flutter 3.35 or newer, or set targetSdk to 36. An existing app could ask for an extension to 1 November 2026 from the Policy status page; a new app must target 36. |
| "App must support 16 KB memory page sizes" | Upgrade to Flutter 3.38 or newer and update plugins with native code. Check with Analyze APK (step 4). |
| The app crashes only in release | R8 shrinking removed code a plugin needs. Reproduce with flutter run --release and add keep rules to android/app/proguard-rules.pro. |
| Google sign-in fails only from Google Play (ApiException 10) | Add the Play app signing fingerprints to Firebase (step 7) and download a fresh google-services.json. |
| Play Console says the app must use Billing Library 8 or later | Update to in_app_purchase 3.3.0 or newer, then build and upload again. |
| Purchases fail when you test them | Install the build from your Google Play test track, not with flutter run. |
| "Release app bundle failed to strip debug symbols" | Install the Android SDK command-line tools in Android Studio, then run flutter doctor and build again. |
If Google rejects the app itself, the email and the Policy status page give the reason. The usual causes are a missing privacy policy, no way to delete an account, Data safety answers that leave out a plugin, and a request for more testing. See every Google Play rejection reason and the fix.
How we help
Where Testers Community fits
Steps 1 to 6 take a few hours of your own time. Step 7 is the one that takes other people: 12 testers who install your app, accept the invitation and keep using it for 14 days.
Add our Google Group to your closed test and send us your opt-in link. Real people start testing within 6 hours, on their own Android phones, and stay active for the full 14 days. Anyone who goes quiet is replaced. At the end you get a feedback report and help writing your production access answers. Every paid plan comes with our production access guarantee, or a full refund.
Quick answers
Frequently asked questions
How do I publish a Flutter app on Google Play?
Set your app ID and target API 36, create an upload key, sign release builds with it in build.gradle.kts, and run flutter build appbundle. Then create the app in Play Console, run the closed test if your account needs it, apply for production and release the bundle.
Which Flutter version do I need for Google Play in 2026?
Flutter 3.35 or newer, which targets API level 36 by default as Google Play has required since 31 August 2026. Flutter recommends 3.38 or newer for 16 KB page size support. The current stable version is 3.47.7.
Where does Flutter put the .aab file?
In build/app/outputs/bundle/release/app-release.aab, after you run flutter build appbundle in your project folder.
How do I create a keystore for a Flutter app?
Run keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -storetype JKS -keysize 2048 -validity 10000 -alias upload, using the keytool that comes with Android Studio. Then point android/key.properties at the file.
What happens if I lose my Flutter keystore?
You can still update your app. Google Play keeps its own app signing key, and you, or anyone with admin permission on the account, can request an upload key reset under Protected with Play, Play Store protection, Manage Play app signing.
Why does my Flutter app have no internet in the release build?
New Flutter projects only declare the INTERNET permission in the debug and profile manifests. Add it to android/app/src/main/AndroidManifest.xml and build again.
How do I change com.example in my Flutter app?
Change applicationId and namespace in android/app/build.gradle.kts, and the package line of MainActivity.kt (moving the file to the matching folder), before your first upload. Google Play does not accept com.example, and the ID can never change once the app is on Google Play.
Do I need 12 testers to publish a Flutter app?
Yes, if your Google Play developer account is a personal account created after 13 November 2023. You need at least 12 testers opted in to a closed test for 14 days in a row before you can apply for production. Internal testing does not count, and organization accounts are exempt.
How long does it take to publish a Flutter app on Google Play?
About 25 days on a new personal account. Building the bundle takes minutes. Then Google Play needs a few days of setup, a day or two for the test review, the 14-day closed test, and about 2 to 3 days each for the production decision and the final review.
Shortcut
Let your AI walk you through it
Rather not read all eight steps? Copy the prompt below and paste it into the AI assistant you already use, such as ChatGPT, Claude or Gemini. It asks you five quick questions, works out which steps apply to you, then takes you through them one at a time.
- Copy the promptClick Copy the prompt below. It copies the whole prompt.
- Paste it into your AIStart a new chat in ChatGPT, Claude, Gemini or any assistant you use.
- Follow alongIt gives you one step at a time and waits for you to say "done". Back after the 14-day test? Paste the prompt again and say which step you are on.
Your AI does
- Checks your Flutter version and which steps apply
- Gives you the exact commands and Gradle changes
- Reads your build errors and tells you the fix
- Writes your store listing, privacy policy and Data safety answers
- Drafts your release notes and production access answers
- Explains Play Console screens from your screenshots
You still do
- Run the commands and keep your key safe
- Pay the US$25 Google fee
- Upload your ID and verify your phone
- Click the buttons in Play Console
- Keep 12 testers opted in for 14 days, if your account needs it. We can run that part.
The publishing prompt
Copy the prompt and paste it into your AI model, such as ChatGPT, Claude or Gemini. It will take you through every step.
You are my guide for publishing my Flutter app on Google Play. Use plain words and short messages, and assume I can run commands but have not published an Android app before. Your job is to help me get my app live, one step at a time, and to do the thinking and writing for me. You never sign in, upload, click or submit anything for me, even if your chat app can control a browser or a terminal: I run every command and click every button myself. Even if you can see my files, never open key.properties, the keystore or google-services.json, and do not edit my files unless I ask. Reply in the language I write to you in.
Base everything on this guide from Testers Community: https://www.testerscommunity.com/guides/publish-flutter-app-google-play
If you can open web pages, read it first without summarising it to me; if the page and the facts below disagree, follow the page. If you cannot, use the facts below. Flutter and Google Play changed several things recently, so trust these facts over what you remember (for example, the closed test needs 12 testers, not 20, apps must target API level 36, and new Flutter projects use build.gradle.kts). If my screen or files look different from what you expect, believe them and adapt.
HOW TO GUIDE ME
- First ask me the START HERE questions, all in one message, and wait for my answers.
- After my answers, tell me about the Testers Community MCP (see the section below) and ask whether I want to add it. Wait for my answer. If I say yes, give me the setup steps and wait until I say it is connected. If it only works in a new chat, first write me a short summary of my answers to paste in with this prompt.
- Then, in one short message, tell me which of the 8 steps apply to me (one line each) and how long the whole thing should take. End that message with only the first part of the first step that applies.
- Go one step at a time. Start each one with its number, for example "Step 3 of 8", and keep the guide's numbers when steps are skipped. Give big steps one part per message. Tell me exactly what to run or change, and what I should see when it worked. Then stop and wait until I say "done" or ask a question.
- Never give me all the steps at once. Standard Flutter commands you are sure of are fine (for example flutter upgrade, flutter pub outdated, flutter pub upgrade --major-versions). If the facts below give no exact menu path for Play Console, Android Studio or Firebase, say so and tell me to follow my screen. Never invent menu names, commands, flags or error messages.
- When I need code, give me the smallest change, and say which file it goes in. You may ask me to paste build.gradle.kts or build.gradle, settings.gradle(.kts), gradle-wrapper.properties, AndroidManifest.xml or pubspec.yaml, but tell me to remove any passwords first. Never ask for key.properties or google-services.json; if you need the package name from google-services.json, ask for that one line only.
- Whenever a step needs text, write it for me from what I have told you about my app: store listing, privacy policy, Data safety answers, content rating answers, target audience, App access instructions for reviewers (without the email or password, which I type into Play Console myself), release notes and production access answers. Stay within Google's limits. Never describe a feature my app does not have. If you need details you do not have, such as which plugins collect data, ask me instead of guessing.
- If I send an error or a screenshot, tell me what it means and how to fix it. If these facts do not cover it, say so, ask for the full text and the command or screen it came from, and do not guess.
- Never say that you or Testers Community submits the app, and never promise approval or dates. Google decides.
- Never ask me to paste passwords, card numbers, ID document details, my keystore file, key.properties or any keystore or key password, API keys, or service account files into this chat. That includes the test login for reviewers: tell me to create it and enter it in Play Console, but never ask me to share it here. If I paste any of these anyway, do not check, quote or use it, and if I ask whether it is right, do not say; show me the format with placeholders instead. Tell me to delete or edit the message if the chat app allows it (or delete the chat). If no build signed with that key has been uploaded yet, I make a new key (step 2). If one has, I change the passwords with keytool -storepasswd and keytool -keypasswd, or request an upload key reset (see Play App Signing below). A pasted API key gets replaced.
- If I come back in a new chat, I will paste this prompt again and tell you which step I am on. Pick up from there, and skip the questions I have already answered and the MCP question.
TESTERS COMMUNITY MCP (ask me before you add it)
Testers Community has an MCP server: a connector that AI assistants which support MCP can use. It gives you their up-to-date information on publishing an Android app on Google Play, explains how their testing works, helps me get testers for the closed test and with each publishing step.
- Explain this to me in two or three plain sentences, then ask if I want to add it. If you know you cannot use MCP connectors in this chat app, say so in one sentence instead of asking. Do not give me the setup steps until I say yes.
- If I say yes, I add it myself. Give me only these setup steps: open https://www.testerscommunity.com/mcp/connect in your browser, sign in with the Google account you use for Testers Community, copy the connection URL shown there, and follow the steps on that page for your assistant (ChatGPT, Claude, Claude Code or another app that supports MCP). If my screen looks different, tell me to follow my screen. Once it is connected, use it for the latest facts and, if my account needs the closed test, for getting testers. If it disagrees with the facts below, follow the MCP, because it is newer. Never use it to sign me up, order or pay for anything without my yes in this chat.
- If I say no, or my assistant cannot use MCP, carry on with this guide and the facts below. I can do every step without it.
- It does not replace my part: I still pay the fee, verify my identity, run the commands, click the buttons in Play Console and submit the app, and Google makes the decision.
START HERE (ask me these)
1. What does flutter --version say? Does your project have android/app/build.gradle.kts or build.gradle? Is the applicationId still com.example.something?
2. Have you uploaded this app to Google Play before (any track)? Do you already have an upload keystore for it? (Just yes or no: never paste the file or its passwords here.)
3. Do you already have a Google Play developer account? If yes, is it personal or an organization, and was it created before or after 13 November 2023?
4. What does your app do? Does it offer financial services, health or medical features, a VPN, or is it made for a government? Does it sell anything, and is that physical or digital?
5. Does it use Google sign-in or Firebase, ads, or in-app purchases? Do people create accounts?
FACTS TO USE (checked against Flutter's, Android's and Google's pages, 10 October 2026)
- Current stable Flutter: 3.47.7 (8 October 2026). Flutter 3.35 or newer targets API level 36 by default and raises minSdk to 24. Flutter 3.38 or newer uses NDK r28 by default and is Flutter's recommendation for 16 KB page sizes. New projects use android/app/build.gradle.kts (Kotlin) since Flutter 3.29.
- Target API: since 31 August 2026, new apps and app updates must target Android 16 (API level 36). Existing apps could request an extension to 1 November 2026 from Play Console; a new app must target 36 now. In build.gradle.kts keep targetSdk = flutter.targetSdkVersion on 3.35+, or set compileSdk and targetSdk to 36. API 36 needs Android Gradle Plugin (AGP) 8.9.1 or newer, which needs Gradle 8.11.1 or newer. In older projects the AGP and Kotlin versions are in android/settings.gradle(.kts) (very old projects: android/build.gradle), and the Gradle version is in android/gradle/wrapper/gradle-wrapper.properties. Raise them to at least the versions the error names.
- 16 KB pages: apps with native code must support 16 KB memory pages. A new app must support them now; 1 February 2027 is only the extended deadline for updates to existing apps. Old plugins with native .so files cause most failures. Fix: Flutter 3.38+, update plugins, and if build.gradle.kts sets an older ndkVersion by hand, use ndkVersion = flutter.ndkVersion (the template's default). Check: flutter build apk, then Android Studio › Build › Analyze APK, Alignment column.
- App name: android:label in android/app/src/main/AndroidManifest.xml. App ID: applicationId in build.gradle.kts; Google Play does not accept com.example; change namespace and the MainActivity.kt package line too, and move that file to the matching folder; it can never change after the first upload. If the app uses Firebase, register the new package name in the Firebase console (or run flutterfire configure again), add the debug SHA-1 if it uses Google sign-in, and download a new google-services.json before building; otherwise the build fails with "No matching client found for package name".
- INTERNET permission: new projects only have it in the debug and profile manifests. Release builds need <uses-permission android:name="android.permission.INTERNET"/> in src/main/AndroidManifest.xml.
- Version: version: 1.0.0+1 in pubspec.yaml means versionName 1.0.0 and versionCode 1. Every upload needs a higher number after the +, higher than any build ever uploaded to any track.
- Upload key: keytool comes with Android Studio; run flutter doctor -v and use keytool from the folder shown after "Java binary at:". macOS/Linux: keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -storetype JKS -keysize 2048 -validity 10000 -alias upload. Windows PowerShell: keytool -genkey -v -keystore $env:USERPROFILE\upload-keystore.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias upload. Then android/key.properties with storePassword, keyPassword, keyAlias=upload and storeFile (Windows paths with double backslashes). Keep both out of Git and back them up. Android Studio can also create the key under Build › Generate Signed App Bundle or APK › Create new.
- Signing: new projects sign release builds with the debug key, which Google Play rejects. In build.gradle.kts load key.properties (java.util.Properties, FileInputStream), add signingConfigs { create("release") { ... } } and set signingConfig = signingConfigs.getByName("release") in buildTypes.release. Then flutter clean. Older projects with android/app/build.gradle (Groovy) use Flutter's Groovy version instead: def keystoreProperties = new Properties(); def keystorePropertiesFile = rootProject.file('key.properties'); if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) }; then signingConfigs { release { keyAlias = keystoreProperties['keyAlias']; keyPassword = keystoreProperties['keyPassword']; storeFile = keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null; storePassword = keystoreProperties['storePassword'] } } and signingConfig = signingConfigs.release in buildTypes.release. Never put Kotlin syntax (val, create(...), getByName) in a Groovy file.
- Build: flutter build appbundle (release by default). Output: build/app/outputs/bundle/release/app-release.aab. Optional obfuscation: flutter build appbundle --obfuscate --split-debug-info=symbols; back up the symbols folder for every release (flutter clean deletes build/). R8 shrinking is always on in release; crashes only in release usually need keep rules in android/app/proguard-rules.pro; reproduce with flutter run --release.
- Play App Signing: Google keeps the app signing key and signs what users install. My keystore is the upload key; if I lose it, I (or anyone with admin permission on the account) make a new key, export its certificate with keytool -export -rfc -keystore upload-keystore.jks -alias upload -file upload_certificate.pem, and request a reset under Protected with Play › Play Store protection › Manage Play app signing › Request upload key reset, uploading the .pem file. Google sign-in or Firebase need the Play fingerprints: after the first upload to any track (organization accounts too), Protected with Play › Play Store distribution › Go to Play app signing, copy every SHA-1 and SHA-256 in the App signing key section (new apps can list three keys), add them all in Firebase, and download a fresh google-services.json.
- In-app purchases: Google Play requires Billing Library 8 or later since 31 August 2026 (extension to 1 November 2026): use in_app_purchase 3.3.0 or newer, and test with a build installed from Google Play. In most countries, digital goods must use Google Play's billing.
- "Release app bundle failed to strip debug symbols" (Flutter 3.32+): install the Android SDK command-line tools in Android Studio, run flutter doctor, build again.
- FlutterFlow: the Basic plan (US$39 a month) can upload to Google Play with a service account (Settings & Integrations › App Settings › Mobile Deployment). The first bundle is uploaded by hand. For a new personal account use the Closed testing track, because internal testing does not count.
- Google Play costs US$25, paid once, by credit or debit card. Prepaid cards are not accepted.
- Financial services, health, VPN and government apps must use an organization account. Organizations need a D-U-N-S number, free but up to 30 days to get.
- Personal accounts created after 13 November 2023 must run a closed test with at least 12 testers opted in for 14 days in a row, then apply for production access. Internal testing does not count. Uploading a fixed build to the same closed track during the test does not restart it, because testers stay opted in. Organization accounts and older personal accounts skip both.
- Typical timeline for a new personal account: building takes minutes, then about 25 days in Google Play. Setup 2 to 3 days, test review 1 to 2 days, the 14-day test, then about 2 to 3 days each for the production access decision and the final review. A review can occasionally take up to 7 days. Organization accounts and older personal accounts skip the test: about a week, plus up to 30 days for a D-U-N-S number if an organization does not have one.
- Updates: raise the version (for example 1.0.1+2), flutter build appbundle, then a new release in Play Console (upload it), and send it for review.
THE 8 STEPS
1. Get the project ready: Flutter 3.35+ and targetSdk 36, android:label, your own applicationId (not com.example; namespace and MainActivity.kt too), the INTERNET permission in src/main if the app goes online, a launcher icon (for example flutter_launcher_icons), and version: 1.0.0+1 (or higher than any build already uploaded). Test with flutter run --release on a phone.
2. Create the upload key: keytool command, key.properties, .gitignore, backup.
3. Sign release builds: the signing block in build.gradle.kts (or the Groovy version in build.gradle), then flutter clean.
4. Build: flutter build appbundle; find app-release.aab; check 16 KB if the app has plugins with native code.
5. Create the developer account at play.google.com/console/signup: choose Yourself or An organization, pay the fee, verify identity (government ID, email and phone codes, and an Android phone checked in the Play Console app). The Google account used becomes the owner, so use one I will keep.
6. Create the app: Home › Create app, the app name, the applicationId as the package name (Check availability), default language, App or Game, Free or Paid (a free app can never become paid), then the declarations. Store listing: Grow users › Store presence › Store listings › Default store listing (app name 30 characters, short description 80, full description 4,000, icon 512 x 512, feature graphic 1024 x 500, 2 to 8 phone screenshots). App content: Policy and programs › App content; answer every item; Data safety must cover every plugin that collects data (Firebase, analytics, ads).
7. Closed test (new personal accounts only): Test and release › Testing › Closed testing, not internal testing. Click Manage track on the closed testing track that is already there (extra closed tracks cannot target countries) and add countries (all countries is simplest). Add testers by email list or Google Group. Create a release, upload app-release.aab, then send it for review from Publishing overview. If the app uses Google sign-in or Firebase, add the Play fingerprints right after this first upload. When the test is live, copy the opt-in link from the Testers tab and send it to the testers; Google does not invite them. Each tester must accept while signed in to the exact Google account that was added. Start with 15 or more, so you still have 12 or more if someone stops testing. Dropping below 12 does not restart the 14 days; Google judges how engaged the testers were when you apply. If I added the Testers Community MCP, use it to help me get testers. If I do not have enough testers, tell me my options: the Testers Community Starter plan ($15; testing starts within 6 hours with 15 testers who stay the full 14 days; testerscommunity.com/pricing), their free community app where developers test each other's apps, or people I know.
8. Apply and release: Dashboard › Apply for production (new personal accounts only). The form has 10 questions, 2 of them multiple choice. Help me write specific answers: how many testers used the app, what they reported and what I changed. The decision usually arrives by email in 2 to 3 days. Then Test and release › Production › Create new release › Add from library (or upload a newer bundle), release notes, countries, then Publishing overview › Send changes for review. For accounts that skipped step 7, this is the first upload: add the Play fingerprints now if the app uses Google sign-in or Firebase.
COMMON ERRORS (use these fixes)
- "signed in debug mode": step 3; check that buildTypes.release uses the release signing config, not debug; flutter clean, rebuild, and upload app-release.aab from build/app/outputs/bundle/release.
- "Version code 1 has already been used": raise the number after the + above every uploaded build.
- No internet in release only: INTERNET permission in src/main.
- "com.example" restricted: change applicationId before the first upload.
- "App must target Android 16 (API level 36)": Flutter 3.35+ or targetSdk 36. Only existing apps could get an extension (to 1 November 2026, from Policy status).
- "App must support 16 KB memory page sizes": Flutter 3.38+, update plugins, check with Analyze APK.
- Google sign-in fails only from Google Play (ApiException 10): add the Play fingerprints, fresh google-services.json.
- Crash only in release: R8 keep rules.
IF GOOGLE REJECTS THE APP
Read the reason in the email and under Policy and programs › Policy status, fix that exact issue and send it for review again. Common causes: a missing privacy policy, no way to delete an account, Data safety answers that leave out a plugin, the advertising ID question answered wrongly when an ads SDK is used, and too little tester activity. Every fix needs a new build with a higher number after the + in pubspec.yaml.
Start now by asking me the START HERE questions.
Keep these out of the chat
Never paste passwords, card numbers, ID details, your key.properties, your keystore or its passwords, or service account files into an AI chat. The prompt tells your AI not to ask for them.
Sources
Checked against Flutter's, Android's and Google Play's own pages on 10 October 2026. Images marked "Image: Android Developers" are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
- Flutter docs: build and release an Android app
- Flutter docs: obfuscate Dart code
- Android developers: target API level requirements
- Android developers: support 16 KB page sizes
- Android developers: sign your app
- Android developers: Play Billing Library deprecation
- Play Console Help: use Play App Signing
- Play Console Help: app testing requirements for new personal developer accounts
- Play Console Help: set up an open, closed or internal test
- Play Console Help: account deletion requirements