Flutter tutorial

Publish a Flutter app: release build and store checklist

This guide prepares a Flutter project for App Store and Google Play submission through Shiptavio. It covers identity, permissions, release builds, the files to send, and the failures that most often block a store upload.

By Shiptavio · Updated 26 October 2026 · 10 min read · Flutter

1. Confirm the app is a fit

Shiptavio publishes free apps under the Apple Developer and Google Play organization accounts of our Malaysia-based organization. Check the commercial and category limits before you spend time on the build.

  • The app is free. No paid download, in-app purchase, or digital subscription.
  • The app is not in a prohibited category such as gambling, crypto or finance, medical, VPN, adult content, or government services.
  • Every API uses production HTTPS. Localhost, private LAN addresses, and test endpoints are rejected.
  • You own the code, name, logo, and content.
  • You can provide a demo account if the app has a login.

Read the prohibited apps page before you start. A store rejection is not the only risk; the publisher account has to stay in good standing.

2. Set the app identity and version

Set the bundle identifier and package name once. Changing either after the first publish creates a separate app in the store. The version line in pubspec.yaml holds both the version name and the build number.

  • iOS bundle identifier: set it in Xcode or in the Runner target settings.
  • Android applicationId: set it in android/app/build.gradle or build.gradle.kts.
  • Build number: increment the value after the + for every store upload.
  • Keep the app name, icon, and splash screen consistent with the store listing.
pubspec.yaml
name: my_app
version: 1.2.0+42
Android application ID
// android/app/build.gradle.kts
android {
  defaultConfig {
    applicationId = "com.yourcompany.myapp"
    versionCode = 42
    versionName = "1.2.0"
  }
}

3. Add permissions and native settings

A Flutter plugin can add native permissions without making the required declaration obvious. Check both platforms before you build.

  • Add an iOS usage description in ios/Runner/Info.plist for every protected resource the app uses: camera, photo library, microphone, location, contacts, notifications, or tracking.
  • Add Android permissions to android/app/src/main/AndroidManifest.xml, and keep the runtime permission requests in Dart.
  • If the app uses deep links, configure the URL scheme and associated domains or app links.
  • If the app supports tablets or landscape, check the iOS device family and Android screen support.
  • Remove test keys, debug banners, and test data from release builds.

4. Prepare icons, screenshots, and listing text

The stores review the listing as closely as the build. Use real screenshots from the app, not website mockups.

  • iOS app icon: 1024 x 1024 px, no transparency.
  • Android app icon: 512 x 512 px. Android feature graphic: 1024 x 500 px.
  • iPhone screenshots at a current store size, plus iPad screenshots if the app runs on iPad.
  • Android screenshots and a short description within the current character limits.
  • A privacy policy, support URL, support email, and a working demo account if the app has a login.
  • Account deletion inside the app if users can create an account.

Artwork generated by flutter_launcher_icons or flutter_native_splash is fine, but check the final assets at full size before you upload them.

5. Run the Flutter checks

Run the same checks a release build will hit. Fix analyzer errors and failing tests before you send the source.

  • Run the app on a small Android screen and a recent iPhone.
  • Test a denied permission, a failed network request, and the offline state.
  • Sign in with the demo account and complete the main task.
  • Check the Android back button and the iOS back gesture.
Checks
flutter pub get
flutter analyze
flutter test

6. Run the release builds

A store build needs a signed iOS archive and an Android App Bundle. Shiptavio runs these commands on its Mac, but a local compile check catches project errors early.

  • If you use flavors, add --flavor and the matching target file.
  • If you pass configuration with --dart-define, send the exact values. They are compiled into the app and are not secret.
  • For a compile check without signing, use flutter build ios --release --no-codesign.
  • Do not send the build/ or .dart_tool/ output.
iOS release build
flutter build ipa --release
Android App Bundle
flutter build appbundle --release

Do not put private API keys in --dart-define. Anything compiled into the app can be extracted.

7. Package the source

Send readable source. A repository URL or a zip of the project is enough. The native folders must be present.

  • Include ios/, android/, pubspec.yaml, pubspec.lock, lib/, and assets.
  • Leave out build/, .dart_tool/, ios/Pods/, android/.gradle/, .env files, keystores, and private keys.
  • If you use a private repository, add the Shiptavio account shown in the submission form as a read-only collaborator.
  • The source limit is 200 MB.
Create a source archive
git add ios android pubspec.lock
git commit -m "Prepare native projects"
git archive --format=zip -o my-app-source.zip HEAD

8. Submit to Shiptavio

Create the app record, choose Flutter, and add the source and store details. A person reviews the project before the build starts.

  • Choose iOS, Android, or both. Apple platforms are optional and use a separate platform credit.
  • Add the exact build command, flavor, and non-secret environment values.
  • Upload the icon, screenshots, feature graphic, and listing text.
  • Complete the age rating, privacy, data safety, and review contact sections.
  • Add a demo account and reviewer notes if the app needs them.

9. Updates and common failures

Your first publish uses one release credit. Each later update uses one more. A store rejection or a fix to the same release does not use an extra credit.

  • Missing ios/ or android/: run flutter create . in the project root, then commit both folders.
  • CocoaPods failure: run pod repo update and pod install in ios/ on a Mac if you can, or send the full error.
  • Missing usage description: the iOS build may pass but App Review will reject the app when a permission prompt appears.
  • Build number not incremented: both stores reject a duplicate build number.
  • Minimum SDK mismatch: check the Android minSdkVersion required by your plugins.
  • Do not change the bundle identifier between updates unless you intend to create a new app.

Common questions

Can I compile-check a Flutter iOS build without a Mac?
Not locally. Xcode is macOS-only. Shiptavio runs the iOS release build on its Mac, so you can send source without owning one. You still need a device or a cloud device service to test the app.
Should I send build/ or .dart_tool/?
No. They are generated and can be large. Send pubspec.lock and the native folders instead.
How do I handle different API URLs for staging and production?
Use --dart-define or a flavor with a production value. Send the exact command and values. Remember that values compiled into the app are visible to anyone who inspects it.
Do I need to send a keystore?
No. Shiptavio signs Android releases with its Play Console key and iOS releases with its Apple certificates. Do not send your own keystore or provisioning profile.
Can I use Flutter flavors?
Yes. Include the flavor name, the entry file, and the exact build command. The build must be reproducible from the source you send.
What happens after I submit?
We review the source and listing, then build and submit. You follow the release status in the dashboard. Apple and Google control their own review timelines.

Related guides

Ready to send the project?

Register the app and add the source. A person reviews the project before the build starts.