Vibe code to app stores

Android & iOS on macOS

14 min read

A step-by-step guide for shipping a vibe-coded Flutter app to the Google Play Store and/or the Apple App Store from a Mac — covering Flutter SDK setup, Xcode and Android Studio configuration, signing, and the full submission process for each store.

If you're using Lunadeck, the build and signing steps are handled for you automatically. This guide is for building and publishing locally, or for understanding what Lunadeck does under the hood.

What you'll need before starting:

  • A Mac running macOS Sonoma 14 or later (Sequoia 15 required for Xcode 26)
  • At least 20 GB of free disk space (Xcode ~12 GB, Android Studio ~8 GB, plus SDKs and simulators)
  • At least 8 GB of RAM (16 GB recommended)
  • A credit/debit card ($25 one-time for Google Play; $99/year for Apple Developer Program)
  • Your exported Flutter project (ZIP from your AI coding tool, or a git clone)
  • About 2–4 hours for initial setup, then 1–4 weeks for publishing

Part 1: Install the Required Tools

You need: Flutter SDK, Xcode (iOS builds), Android Studio (Android builds), Java (JDK 17), and CocoaPods (iOS dependency management). Homebrew handles most of this.

1.1 Install Homebrew

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Follow the printed instructions to add Homebrew to your PATH. On Apple Silicon:

echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

Verify:

brew --version
brew install curl wget unzip git

1.2 Install the Flutter SDK

Option A: Using Homebrew (easiest)

brew install --cask flutter

Option B: Manual install

Download the latest stable Flutter SDK .zip for macOS from https://docs.flutter.dev/get-started/install/macos/mobile-ios, extract it, and add to your PATH:

mkdir -p ~/development
cd ~/development
unzip ~/Downloads/flutter_macos_*.zip
 
echo 'export PATH="$PATH:$HOME/development/flutter/bin"' >> ~/.zshrc
source ~/.zshrc

Verify and run flutter doctor:

flutter --version
flutter doctor

flutter doctor lists everything Flutter needs. Resolve issues as you encounter them in the steps below — it will guide you through each one.

1.3 Install Xcode (iOS builds only)

Step 1: Install from the Mac App Store — search for "Xcode" and install it (~12 GB). Or download from https://developer.apple.com/xcode/.

Step 2: Install command line tools:

xcode-select --install
sudo xcodebuild -license accept

Verify:

xcode-select -p
# Should output: /Applications/Xcode.app/Contents/Developer

Step 3: Open Xcode once to let it install simulators and additional components.

1.4 Install CocoaPods (iOS dependency management)

brew install cocoapods
pod --version

1.5 Install Java (JDK 17) — Android only

brew install openjdk@17
 
sudo ln -sfn $(brew --prefix openjdk@17)/libexec/openjdk.jdk \
  /Library/Java/JavaVirtualMachines/openjdk-17.jdk
 
echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 17)' >> ~/.zshrc
source ~/.zshrc
java -version

1.6 Install Android Studio — Android only

Step 1: Download the macOS .dmg from https://developer.android.com/studio, drag to Applications, and launch it. Run through the setup wizard (choose Standard, let it download the SDK and emulator).

Step 2: Install SDK components — go to Tools > SDK Manager:

  • SDK Platforms: check Android 14 (API 34) or newer
  • SDK Tools: check Android SDK Build-Tools, Android SDK Command-line Tools, Android Emulator, Android SDK Platform-Tools

Step 3: Set environment variables — add to ~/.zshrc:

export ANDROID_HOME="$HOME/Library/Android/sdk"
export ANDROID_SDK_ROOT="$HOME/Library/Android/sdk"
export PATH="$PATH:$ANDROID_HOME/platform-tools"
export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin"

Reload and verify:

source ~/.zshrc
adb --version

Apple Silicon note: When creating Android virtual devices, pick ARM-based system images (not x86).

Step 4: Accept Android licenses:

flutter doctor --android-licenses

Type y at each prompt.

1.7 Final check

flutter doctor

All items for your target platforms should show a green checkmark. The "Chrome" and "VS Code" entries are optional and can be ignored for mobile publishing.


Part 2: Export and Prepare Your Project

2.1 Get your project files

If your AI tool provides a ZIP export:

mkdir ~/my-app
cd ~/my-app
unzip ~/Downloads/your-project.zip -d .

If you're working from a git repository:

git clone https://github.com/yourname/your-app.git ~/my-app
cd ~/my-app

2.2 Get dependencies

cd ~/my-app
flutter pub get

For iOS, also install CocoaPods dependencies:

cd ios
pod install
cd ..

2.3 Check your app configuration

Open pubspec.yaml and confirm the version and app name:

name: my_app
version: 1.0.0+1   # versionName+versionCode

Check the applicationId in android/app/build.gradle:

defaultConfig {
    applicationId "com.yourname.myapp"
    ...
}

Check the bundle ID in ios/Runner/Info.plist or in Xcode (App target > General > Bundle Identifier):

com.yourname.myapp

Both IDs are permanent. Change any generic com.example.* values now, before your first store upload.


Part 3: Test Your App

3.1 Test on Android

In Android Studio > Device Manager, create a virtual device (Pixel 7, API 34+, ARM image on Apple Silicon). Start it, then:

flutter run -d android

Or run flutter devices first to see available targets.

3.2 Test on iOS

Simulator:

flutter run -d ios

Flutter auto-selects the running iOS Simulator. Or specify one:

flutter run -d "iPhone 16"

Physical device: Connect your iPhone. In Xcode, select the Runner target > Signing & Capabilities > select your Apple team. A free Apple ID works for testing (builds expire every 7 days; the paid Developer Program is required for App Store distribution).

Then:

flutter run -d YOUR_DEVICE_ID

3.3 Common issues

"No devices found" — run flutter devices. For the emulator, make sure it's fully booted. For iOS Simulator, make sure it's open: open -a Simulator.

iOS build fails with "No signing certificate" — select an Apple team in Xcode > Signing & Capabilities.

"pod: command not found"brew install cocoapods

pod install fails — try pod repo update first. If a plugin is incompatible with your CocoaPods version, check the plugin's documentation for workarounds.

Gradle errors (Android) — run flutter clean and rebuild.


Part 4: Prepare for Release

4.1 Set your app icon

Add flutter_launcher_icons to pubspec.yaml:

dev_dependencies:
  flutter_launcher_icons: ^0.14.0
 
flutter_launcher_icons:
  android: true
  ios: true
  image_path: "assets/icon.png"
  adaptive_icon_background: "#FFFFFF"
  adaptive_icon_foreground: "assets/icon-foreground.png"

Place your icon (at least 1024×1024 px, no transparency, no rounded corners) at assets/icon.png, then:

flutter pub get
dart run flutter_launcher_icons

iOS icon note: Apple applies rounded corners automatically. Your source image must be square with no transparency and no pre-applied rounding.

4.2 Set your splash screen

dev_dependencies:
  flutter_native_splash: ^2.4.0
 
flutter_native_splash:
  color: "#FFFFFF"
  image: assets/splash.png
  android: true
  ios: true
flutter pub get
dart run flutter_native_splash:create

4.3 Update version numbers

In pubspec.yaml:

version: 1.0.0+1
#         ^^^^^  versionName / CFBundleShortVersionString
#               ^ versionCode / CFBundleVersion — must increment with every store upload

Flutter writes these to both the Android and iOS native projects at build time.

4.4 Configure permissions

Android — in android/app/src/main/AndroidManifest.xml:

<!-- Internet access — required for most apps -->
<uses-permission android:name="android.permission.INTERNET" />
 
<!-- Add only what your app actually uses: -->
<!-- <uses-permission android:name="android.permission.CAMERA" /> -->
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> -->

iOS — in ios/Runner/Info.plist, add usage description strings for each permission. Apple rejects apps with missing or vague descriptions:

<!-- Add only for permissions your app actually uses: -->
<key>NSCameraUsageDescription</key>
<string>This app uses the camera to take photos for your profile.</string>
 
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses your location to show nearby results.</string>
 
<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone for voice messages.</string>
 
<key>NSPhotoLibraryUsageDescription</key>
<string>This app accesses your photo library to let you choose images.</string>

Part 5: Sign and Build the Android Release Bundle

5.1 Generate a signing key

keytool -genkey -v -keystore ~/my-app-release.keystore \
  -alias my-app-key \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000

Back up this keystore file and both passwords. Losing it means you can never update your app on Google Play.

5.2 Configure Flutter signing

Create android/key.properties (do NOT commit this to git — add it to .gitignore):

storeFile=/Users/YOUR_USERNAME/my-app-release.keystore
storePassword=your_keystore_password
keyAlias=my-app-key
keyPassword=your_key_password

Edit android/app/build.gradle — add above the android { block:

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

Inside the android { block:

android {
    // ... existing config ...
 
    signingConfigs {
        release {
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
        }
    }
 
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

5.3 Build the Android release bundle

flutter build appbundle --release

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


Part 6: Sign and Build the iOS Release Archive

iOS code signing requires an Apple Developer Program membership ($99/year).

6.1 Enroll in the Apple Developer Program

  1. Go to https://developer.apple.com/programs/
  2. Click "Enroll" and sign in with your Apple ID
  3. Choose Individual or Organization
  4. Pay the $99/year fee
  5. Wait for identity verification (a few hours to a few days)

6.2 Configure signing in Xcode

open ios/Runner.xcworkspace   # Use .xcworkspace, not .xcodeproj
  1. Select the "Runner" target in the project navigator
  2. Go to Signing & Capabilities
  3. Check "Automatically manage signing" and select your Apple Developer team

6.3 Build the iOS release

Option A: Flutter CLI (easiest)

flutter build ipa

Flutter builds the archive and exports an .ipa file to build/ios/ipa/. It uses the signing configuration from Xcode.

If flutter build ipa fails with a signing error, open Xcode and confirm your team and provisioning profile are set correctly first.

Option B: Xcode GUI

  1. In Xcode, set the device target to "Any iOS Device (arm64)" — not a simulator
  2. Go to Product > Archive
  3. When the Organizer opens, select your archive and click Distribute App
  4. Choose App Store Connect > Upload and follow the prompts

Option C: Command line (advanced)

xcodebuild -workspace ios/Runner.xcworkspace \
  -scheme Runner \
  -configuration Release \
  -archivePath ~/my-app.xcarchive \
  -destination 'generic/platform=iOS' \
  archive

Create ExportOptions.plist:

<?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>method</key>
    <string>app-store</string>
    <key>uploadSymbols</key>
    <true/>
</dict>
</plist>
xcodebuild -exportArchive \
  -archivePath ~/my-app.xcarchive \
  -exportPath ~/my-app-ipa/ \
  -exportOptionsPlist ExportOptions.plist

6.4 Upload to App Store Connect

  • From Xcode Organizer: Distribute App > App Store Connect > Upload (after Product > Archive)
  • Transporter app: free on the Mac App Store — drag and drop your .ipa
  • Flutter CLI: flutter build ipa followed by opening the Organizer from Xcode's Window menu

Part 7: Developer Accounts and Store Listings

7.1 Google Play Developer Account

  1. Go to https://play.google.com/console and sign in
  2. Pay the one-time $25 USD fee and complete identity verification
  3. Wait 24–48 hours for activation

Personal accounts created after November 2023 require device verification — install the Play Console app on an Android device and follow the steps.

7.2 App Store Connect

  1. Go to https://appstoreconnect.apple.com and sign in
  2. Click + > New App
  3. Fill in: Platform (iOS), Name, Primary language, Bundle ID (must match your Xcode project's bundle ID), and an internal SKU

Required fields:

  • Screenshots — at minimum for 6.7" (iPhone 15 Pro Max) and 6.1" (iPhone 15 Pro); capture from the Simulator
  • App icon — 1024×1024 px, no transparency, no rounded corners
  • Privacy policy URL — mandatory; a Notion or GitHub Pages page works
  • Age rating — fill out the content questionnaire

Part 8: Google Play — The Closed Testing Requirement

New personal developer accounts cannot publish directly to production. Google requires:

  1. At least 12 testers opted in to a closed test track
  2. Those testers remain opted in for 14 consecutive days
  3. Then you can apply for production access

Set up closed testing

  1. In Play Console, click Create app and complete all required sections
  2. Go to Testing > Closed testing, create a tester list with 12+ Gmail addresses
  3. Click Create new release, accept Play App Signing, upload your .aab, click Start rollout
  4. Share the opt-in link — testers must install the app on a real Android device

After 14 days, apply for production access from the app Dashboard.


Part 9: App Store — Review Process

Flutter apps are fully native — they don't use a WebView — so you won't encounter the Guideline 4.2 rejection that Capacitor WebView apps commonly face. Apple reviews Flutter apps like any other native app.

What Apple looks for

  • App works correctly and doesn't crash during review
  • All permission usage description strings are present in Info.plist
  • Privacy policy URL is provided if the app collects user data
  • Store listing accurately describes what the app does
  • Demo credentials are provided if the app requires login

TestFlight

Before submitting to production, test with TestFlight:

  1. Upload a build via flutter build ipa or Xcode Organizer
  2. In App Store Connect, go to the TestFlight tab
  3. Internal testers (up to 100 team members): no review required, available almost immediately
  4. External testers (up to 10,000): first build of each version requires a brief beta review (~24 hours)

TestFlight builds expire after 90 days.

Submitting for production

  1. In App Store Connect, select your build
  2. Fill out App Review Information — provide demo credentials if your app has login
  3. Click Submit for Review

Most reviews complete within 24 hours.


Part 10: Publish to Production

Google Play

Once you have production access:

  1. Production > Create new release
  2. Upload your .aab, add release notes, select distribution countries
  3. Start rollout to production

App Store

Once Apple approves, choose Immediate, Manual, or Scheduled release.


Part 11: Updating Your App

# 1. Make your code changes
 
# 2. Increment the build number in pubspec.yaml
#    e.g., 1.0.0+1 → 1.0.0+2
#    (both Android versionCode and iOS CFBundleVersion are derived from this)
 
# 3. If iOS dependencies changed:
cd ios && pod install && cd ..
 
# Android
flutter build appbundle --release
# Upload new .aab to Google Play Console
 
# iOS
flutter build ipa
# Upload to App Store Connect via Transporter or Xcode Organizer

Quick Reference

# === SETUP ===
flutter pub get
cd ios && pod install && cd ..
flutter doctor --android-licenses
 
# === TESTING ===
flutter devices
flutter run                      # auto-selects a device
flutter run -d android
flutter run -d ios
 
# === ANDROID RELEASE ===
flutter build appbundle --release
# Output: build/app/outputs/bundle/release/app-release.aab
 
# === iOS RELEASE (CLI) ===
flutter build ipa
# Output: build/ios/ipa/YourApp.ipa
 
# === iOS RELEASE (Xcode GUI) ===
open ios/Runner.xcworkspace
# Product > Archive > Distribute App > App Store Connect > Upload
 
# === CLEAN BUILD ===
flutter clean && flutter pub get

Troubleshooting

"flutter: command not found" — Flutter is not in your PATH. Follow Part 1.2 and run source ~/.zshrc.

"Android licenses not accepted"flutter doctor --android-licenses → type y at each prompt.

"JAVA_HOME is not set" (Android) — add to ~/.zshrc:

export JAVA_HOME=$(/usr/libexec/java_home -v 17)

Gradle fails with "Could not determine java version" — multiple Java versions installed. Confirm JAVA_HOME points to JDK 17 with /usr/libexec/java_home -V.

Xcode build fails with "No signing certificate" — select your Apple Developer team in Xcode > Signing & Capabilities. Paid membership required for distribution.

pod install fails — try pod repo update first. If a plugin is incompatible, check its documentation for workarounds.

flutter build ipa fails with signing error — open Xcode, confirm the signing team is set, and try again. Or use Xcode's Product > Archive for more detailed error output.

iOS Simulator is slow — close other heavy apps. Try Device > Erase All Content and Settings in the Simulator menu.

Dart/Flutter version mismatch — run flutter upgrade to update to latest stable.

"The request was denied by service delegate" — provisioning profile or certificate issue. In Xcode, toggle "Automatically manage signing" off and back on, or go to Settings > Accounts > download profiles.


Realistic Expectations

The Google Play timeline is long. The 14-day testing requirement plus review means 3–4 weeks from start to live. The App Store is faster but rejections add cycles.

Flutter gets much easier App Store approval than WebView wrappers. Because Flutter renders native-feeling UI compiled to ARM code, Apple doesn't apply the Guideline 4.2 WebView rejection. The App Store bar is still real — your app must work, not crash, and comply with all guidelines — but the WebView problem isn't one you face.

The costs are different. Google Play: $25 one-time. Apple: $99 per year, every year. A lapsed membership removes your apps from the App Store within 24 hours.

AI-generated Flutter code frequently has plugin issues. Flutter plugins often require minimum SDK versions, specific iOS permissions, or pod install after adding them. Budget time for this.

iOS and Android will surface different bugs. Test on both platforms before submitting to either store.