How to Set Up a Flutter App for Dual-Store Publishing in 2026

Published: July 13, 2026

Version française →

Flutter gives you one codebase for iOS and Android. That's the easy part. Getting that codebase onto the App Store and Google Play — correct metadata, properly sized screenshots, the right binary for each store — is where the single codebase splits back into two of everything.


What dual-store publishing actually requires

Running flutter buildtwice isn't the job. Each store has its own binary format (.ipa for Apple, .aab for Google), its own signing credentials (a .p8 API key and provisioning profile for Apple, a service account JSON for Google), its own metadata limits, its own screenshot specs, and its own release model. None of it is Flutter-specific — it's the publishing layer that sits on top of any codebase. Shipping React Native instead? The React Native versionof this guide covers the same ground with RN's build commands.


Step 1: Configure your Flutter project for both platforms

This is the part that's genuinely Flutter-specific — and where Flutter saves you real time over driving Xcode and Gradle by hand. Start with one version in one place: pubspec.yaml feeds both platforms.

# pubspec.yaml
version: 1.0.0+1   # 1.0.0 = version name, +1 = build number

1.0.0 becomes CFBundleShortVersionString on iOS and versionName on Android; the +1 build number becomes CFBundleVersion and versionCode. Override per build with --build-name and --build-numberin CI. The build number has to increase on every upload to either store — it's the one field you'll bump most.

iOS: signing and flutter build ipa

Open ios/Runner.xcworkspace once to set the bundle ID (it must match the app record in App Store Connect) and the distribution certificate plus App Store provisioning profile on the Runner target. After that the build is one command:

flutter build ipa --release --export-options-plist=ios/ExportOptions.plist

The signed .ipa lands in build/ios/ipa/. This step needs macOS — flutter build ipa wraps xcodebuild, so there's no way around a Mac or a macOS CI runner for the iOS binary. You'll also need a .p8 App Store Connect API key for the upload itself (Step 4).

Android: keystore and flutter build appbundle

Generate an upload keystore, then point Flutter at it with android/key.properties (kept out of version control):

# android/key.properties
storePassword=<your-store-password>
keyPassword=<your-key-password>
keyAlias=upload
storeFile=upload-keystore.jks

android/app/build.gradle (or build.gradle.kts on newer projects) reads that file for the release signing config. Then:

flutter build appbundle --release

The signed .aab lands in build/app/outputs/bundle/release/app-release.aab. Unlike the .ipa, this runs on Linux, Windows, or macOS — no Mac required for the Android side. Losing the keystore means you can never update the app on Google Play, so back it up somewhere you trust.


Step 2: Write metadata once, publish to both stores

Same copy, two forms with different field names and limits:

  • Title — 30 characters, on both stores.
  • Subtitle / short description — 30 on the App Store, 80 on Google Play.
  • Long description — 4000 characters, on both.
  • Keywords — a dedicated 100-character field on the App Store; Google Play has none, it indexes the description text instead.

Because the title caps at exactly 30 on both, one title usually works for both — no trimming for one store and padding for the other. The long description is where they diverge: Google Play indexes it for search, the App Store doesn't, so the Play copy should fold keywords in naturally while the App Store copy can just sell. A shared doc is fine for one app and falls apart at two — OneStore's editor shows both stores' live character counters as you type. The full field-by-field breakdown is in App Store Connect vs Google Play Console metadata differences.


Step 3: Prepare screenshots without Photoshop

Apple requires at least one of three iPhone formats — 6.5" (1242×2688), 6.7" (1290×2796), or 6.9" (1320×2868) — not all three. 6.7" is the current standard: Apple auto-scales it into the 6.9" slots, so shipping 6.7" alone clears review. iPad 12.9" (2048×2732) is only required if your app declares iPad support — many Flutter apps do, so check your Info.plist. Google Play wants its own set: phone, 7" tablet, 10" tablet. Design once at the highest resolution and resize from there — automating screenshot resizing covers turning one HD source into every required format.


Step 4: Upload the binaries

Two files, two destinations. The .ipa goes to App Store Connect via Apple Transporter (or altool, or the App Store Connect API) — no Xcode needed once the file exists; uploading an .ipa without Xcode walks through every method. The .aab goes to Google Play through the Developer API, authenticated with your service account JSON, assigned to a track (internal/alpha/beta/production) and committed — uploading an .aab without Android Studio covers the options. OneStore does both from one dashboard: the .ipa through Transporter, the .aab through the Developer API, using credentials you connect once.


Step 5: Manage the rollout

iOS and Android don't release the same way, and it trips up a lot of first dual-store launches.

Apple's phased release is automatic and controlled by Apple: 1%, 2%, 5%, 10%, 20%, 50%, then 100% over seven days, and only from version 2 onward — never the initial submission. You can pause or halt it in App Store Connect, but you can't set the percentage; Apple owns that ramp. OneStore surfaces the current stage on the release timeline and links back to App Store Connect to pause it, rather than pretending to control a number Apple doesn't expose.

Google Play's staged rollout is the opposite — the percentage is yours. Set it, monitor it, halt, resume, push to 100%, and promote a build across tracks (internal → production) without re-uploading, all from the dashboard.


Step 6: Manage reviews across both stores

Reviews land on both stores independently; checking by hand means two consoles open all day. A unified inbox pulls both into one place with AI-drafted replies in the reviewer's language — replying to reviews without two consoles goes deeper on the workflow.


Step 7: Keep listings in sync after launch

Listings drift: someone edits a description straight in a console and your source of truth is quietly wrong. Drift detection diffs each store's live listing against what's in your tool when you open the editor, flagging changes before you overwrite a real edit or push stale copy.


Putting it together

Flutter collapses the build into flutter build ipa and flutter build appbundle. It doesn't collapse the publishing layer — metadata, screenshots, uploads, reviews, and rollout are still two of everything, and that's where the time goes. OneStore covers all of it in one place, both stores, on a free plan that includes 3 apps with no card required. AI credits are only spent on optional features like translation and review replies — connecting stores, editing listings, and publishing stay unlimited and free on every plan.

The Store Listing Audit tool (no account, no email) scores an existing listing out of 100.


FAQs

Does the Flutter or Dart SDK version affect App Store or Play review?

No. Both stores review the compiled binary, not your toolchain — they don't see which Flutter version built it. What matters is that the .ipa is signed with a distribution certificate, the .aab is signed with your upload key, and the bundle ID / application ID match the store records.

What's the difference between the App Store and Google Play title limits?

None — both cap the title at 30 characters, so one title usually works for both stores. (The 50-character figure some guides cite is out of date.)

Where does flutter build put the .ipa and .aab?

flutter build ipa --release writes to build/ios/ipa/; flutter build appbundle --release writes to build/app/outputs/bundle/release/app-release.aab. Both live under the project-level build/ directory, not the platform subfolders.

Do I need a Mac to build a Flutter .ipa?

Yes. flutter build ipa wraps xcodebuild, which only runs on macOS, so the iOS binary needs a Mac or a macOS CI runner. flutter build appbundle for the Android .aab runs on any OS. The upload step, for either, can happen from anywhere once the file exists.

Can I use phased rollout for a Flutter app's first release?

Not on Apple — phased release only applies to updates, from version 2 onward; the first approved release goes to 100% immediately. On Google Play you can start even the first production rollout at a reduced percentage.

How do I set the version and build number for a Flutter release?

In pubspec.yaml as version: 1.0.0+1 — the part before + is the version name (CFBundleShortVersionString / versionName), the part after is the build number (CFBundleVersion / versionCode). Override per build with flutter build --build-name=1.0.1 --build-number=2. The build number must increase on every upload to either store.

Is my .p8 key and service account JSON stored securely?

Yes: credentials (the .p8 key, the service account JSON) are stored in a vault encrypted at rest with AES-256-GCM at the application layer. A Data Processing Agreement based on the EU Standard Contractual Clauses is available at onestore.so/dpa.