Publish Flutter Apps to App Store & Google Play (2026)
TL;DR
- Generate your Android keystore and iOS certificates/provisioning profiles before anything else. Losing a keystore means you cannot update your app
- Apple requires a privacy manifest (PrivacyInfo.xcprivacy) as of 2024. Missing it causes automatic rejection
- The most common rejection reasons are insufficient privacy policy, missing purpose strings for permissions, and screenshots that do not match actual app behaviour
- Plan for 1-3 day review on App Store and 1-7 days on Google Play. First submissions take longer
Table of Contents
- Pre-Submission Checklist
- Android: Building and Signing for Release
- iOS: Certificates, Provisioning, and Archiving
- Flutter-Specific Build Commands
- Store Listing Requirements
- Common Rejection Reasons
- Privacy Manifest Requirements for Apple
- Testing Before Submission
- Timeline Expectations
- Frequently Asked Questions
Pre-Submission Checklist
Complete these items before touching either store console:
- App icon: 1024x1024 PNG (no transparency for iOS, transparency allowed for Android)
- Splash screen configured and tested
- App version and build number set in
pubspec.yaml - All debug logging removed or wrapped in
kDebugModechecks - Privacy policy URL hosted and accessible
- Terms of service URL (required for apps with accounts)
- App name finalised (changing later is possible but affects SEO)
- Minimum SDK versions set (Android: minSdk 21+, iOS: minimum deployment target 13.0+)
- Permissions used are justified with purpose strings
- Crash-free rate verified on test devices
- Release build tested on physical devices (not just simulator/emulator)
Android: Building and Signing for Release
Step 1: Generate a Keystore
keytool -genkey -v -keystore ~/upload-keystore.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias upload
Store this keystore file securely. If you lose it, you cannot publish updates to your app. Back it up to a secure location outside your repository.
Step 2: Configure Signing in Gradle
Create android/key.properties:
storePassword=your_store_password
keyPassword=your_key_password
keyAlias=upload
storeFile=/path/to/upload-keystore.jks
Add to .gitignore:
android/key.properties
*.jks
Update 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
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
Step 3: Build App Bundle
Google Play requires AAB (Android App Bundle) format, not APK:
flutter build appbundle --release --obfuscate --split-debug-info=build/debug-info
The output AAB is at build/app/outputs/bundle/release/app-release.aab.
iOS: Certificates, Provisioning, and Archiving
Step 1: Apple Developer Account
You need an Apple Developer Program membership ($99/year). Enrolment takes 24-48 hours for individual accounts and up to a week for organisation accounts.
Step 2: Create Certificates and Provisioning Profiles
- Go to Apple Developer Portal
- Create an iOS Distribution certificate
- Register your app's Bundle ID
- Create an App Store provisioning profile linked to the certificate and Bundle ID
Step 3: Configure Xcode
Open ios/Runner.xcworkspace in Xcode:
- Select the Runner target
- Set the Bundle Identifier to match your registered Bundle ID
- Select your team under Signing & Capabilities
- Set Build Configuration to Release
- Set the deployment target (iOS 13.0 minimum recommended)
Step 4: Archive and Upload
flutter build ipa --release --obfuscate --split-debug-info=build/debug-info
This creates an .xcarchive file. Open it in Xcode Organizer and click "Distribute App" to upload to App Store Connect.
Alternatively, use the command line:
xcrun altool --upload-app --type ios \
--file build/ios/ipa/*.ipa \
--apiKey YOUR_API_KEY \
--apiIssuer YOUR_ISSUER_ID
Flutter-Specific Build Commands
# Android App Bundle (for Google Play)
flutter build appbundle --release \
--obfuscate \
--split-debug-info=build/debug-info \
--build-name=1.0.0 \
--build-number=1
# iOS IPA (for App Store)
flutter build ipa --release \
--obfuscate \
--split-debug-info=build/debug-info \
--build-name=1.0.0 \
--build-number=1
# For testing: Android APK (sideloading)
flutter build apk --release --split-per-abi
Keep --build-number unique for every upload. Both stores reject uploads with duplicate build numbers.
Store Listing Requirements
Screenshots
Both stores require screenshots of your app running on devices:
- Google Play: Minimum 2 screenshots per device type. Recommended: phone (1080x1920 or 1440x2560) and tablet (1200x1920)
- App Store: iPhone 6.7" (1290x2796), iPhone 6.5" (1284x2778), iPad 12.9" (2048x2732). At minimum, provide the largest size and Apple will scale down
Use real screenshots or high-quality promotional graphics. Do not use mockups that differ from the actual app because reviewers will compare.
App Description
- Short description (Google Play: 80 chars, App Store: promotional text is optional)
- Full description: describe what the app does, its features, and why users need it
- Keywords (App Store only): 100-character keyword field. Use comma-separated terms without spaces after commas
Privacy Policy (Mandatory)
Both stores require a publicly accessible privacy policy URL that describes:
- What data the app collects
- How the data is used
- How the data is stored and protected
- How users can delete their data
Use our README generator to create structured documentation for your app, including privacy policy templates.
Common Rejection Reasons
Google Play
- Missing privacy policy: Required for all apps that access user data, which includes most apps
- Target API level too low: Google Play requires targetSdkVersion 34+ as of late 2024
- Permissions without justification: Using
ACCESS_FINE_LOCATIONwithout a feature that needs precise location - Data safety form incomplete: The data safety section in Play Console must accurately describe all data collection
Apple App Store
- Missing purpose strings: Every permission must have a user-facing explanation in
Info.plist(NSCameraUsageDescription, NSLocationWhenInUseUsageDescription, etc.) - Privacy manifest missing: New requirement since 2024 (see next section)
- Incomplete functionality: Apple rejects apps that feel like demos or have placeholder content
- Guideline 4.3 (Spam): Apps too similar to existing apps without meaningful differentiation
Privacy Manifest Requirements for Apple
Since Spring 2024, Apple requires a PrivacyInfo.xcprivacy file in your iOS app bundle. This file declares:
- Required reasons APIs: If your app uses certain APIs (UserDefaults, file timestamp, disk space, etc.), you must declare why
- Data collection types: What data your app collects and its purposes
- Tracking domains: Third-party domains used for tracking users
Create ios/Runner/PrivacyInfo.xcprivacy:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
</plist>
Flutter apps that use SharedPreferences (which wraps UserDefaults) need at minimum the UserDefaults declaration.
Testing Before Submission
Firebase App Distribution (Both Platforms)
Distribute test builds to testers without store submission:
# Install Firebase CLI
npm install -g firebase-tools
# Upload Android build
firebase appdistribution:distribute build/app/outputs/bundle/release/app-release.aab \
--app YOUR_FIREBASE_APP_ID \
--groups "internal-testers"
TestFlight (iOS)
Upload your build to App Store Connect and create a TestFlight internal testing group. Internal testers (up to 100) get access immediately without review. External testers (up to 10,000) require a brief review.
Google Play Internal Testing
Create an internal testing track in Google Play Console. Upload your AAB and add tester email addresses. Builds are available within minutes for up to 100 testers.
For estimating the full cost of your app development and publishing process, use our software cost estimator.
The Debuggers provides Flutter app development and publishing services, handling the entire process from development through store submission and ongoing maintenance.
For tips on debugging common Flutter issues before submission, see our Flutter errors debugging guide.
Timeline Expectations
| Step | Google Play | App Store |
|---|---|---|
| Account setup | Instant ($25 one-time) | 1-2 days ($99/year) |
| First review | 1-7 days | 1-3 days |
| Subsequent reviews | Hours to 3 days | 1-2 days |
| Rejection appeal | 1-3 days | 1-5 days |
| App goes live | Immediately after approval | Choose date or immediately |
First-time submissions consistently take longer. Expect 3-7 days for your first Google Play submission. Apple is typically faster at 1-3 days but may ask follow-up questions that extend the process.
Frequently Asked Questions
What happens if I lose my Android keystore?
You permanently lose the ability to update your app through the same listing. You would need to create a new app listing with a new package name, losing all existing ratings, reviews, and install count. Google Play does offer app signing by Google Play, where Google holds a copy of the signing key. If you enrolled in Play App Signing, Google can sign new uploads even if you lose your upload keystore. Always enrol in Play App Signing for this safety net.
Can I publish a Flutter app without a Mac?
For Google Play, yes. You can build Android App Bundles on Windows, Linux, or macOS. For the Apple App Store, you need macOS to build the iOS archive and upload to App Store Connect. There is no workaround for this requirement. Cloud-based macOS CI services (Codemagic, GitHub Actions with macOS runners) can handle iOS builds if you do not have a Mac locally.
How long should I test before submitting?
At minimum, test for one full week with real users on multiple devices (different screen sizes, OS versions, and network conditions). For a first release, two weeks of beta testing through TestFlight and Google Play internal testing is recommended. Pay attention to crash-free rates. Both stores surface crash reports and apps with high crash rates may be deprioritised in search results or flagged during review.
Do I need to localise my app for store approval?
Store approval does not require localisation. However, localising your store listing (title, description, screenshots) for your target markets significantly improves download rates. At minimum, provide English store listings. If you target non-English markets, invest in professional translation for the store listing even if the app itself is English-only initially.
Planning your Flutter app project?
Estimate the full development and publishing cost with our free Software Cost Estimator. Get realistic budget ranges based on your app's complexity and feature set.
Need help publishing your Flutter app? The Debuggers handles the entire Flutter development and publishing process, from initial development through store submission.
Found this helpful?
Join thousands of developers using our tools to write better code, faster.