Prerequisites

Before the install/build steps below, make sure your host and any physical test devices are set up. For emulator/simulator-only work you can skip the device sections.

Host toolchain

You want to build for…You need
Android (any OS)Android Studio or the Android command-line tools (SDK + platform-tools), plus a JDK 17+
iOS (macOS only)The full Xcode app from the App Store, not just the standalone Command Line Tools
Appium/WebdriverIO automation (optional)Node.js 18+ (only if you use the Appium harnesses or automation/appium/)

Android needs a JDK (17+): Gradle won’t build without one. Android Studio bundles a suitable runtime, but if you installed only the command-line tools, install a JDK and point JAVA_HOME at it. On macOS a bare java on PATH is just a stub, set JAVA_HOME to a real JDK.

macOS (Homebrew):

1
brew install openjdk@17 && export JAVA_HOME=/opt/homebrew/opt/openjdk@17

Debian/Ubuntu:

1
sudo apt-get install -y openjdk-17-jdk

Windows (winget):

1
winget install --id EclipseAdoptium.Temurin.17.JDK

Point your shell at the SDK + JDK (ANDROID_HOME / JAVA_HOME). If you installed the command-line tools only (no Android Studio), your shell needs three things so adb, emulator, sdkmanager, and Gradle can find the SDK and a real JDK: ANDROID_HOME, JAVA_HOME, and both on PATH. (Android Studio sets ANDROID_HOME for you; a Homebrew openjdk still needs JAVA_HOME.) Verify with flutter doctor, a green Android toolchain means these are set. The Appium harnesses (automation/scripts/appium_run_*.sh, verify_all_modules.sh) auto-resolve all of this, so you only need these exports for the manual adb/emulator/./gradlew commands.

Add the block for your shell to its startup file so it persists across sessions (adjust the SDK path, Homebrew’s is shown; Android Studio installs to ~/Library/Android/sdk on macOS, ~/Android/Sdk on Linux):

macOS (zsh, the default; appends to ~/.zshrc):

1
2
3
4
5
6
cat >> ~/.zshrc <<'EOF'
export ANDROID_HOME="/opt/homebrew/share/android-commandlinetools"
export JAVA_HOME="/opt/homebrew/opt/openjdk@17"
export PATH="$JAVA_HOME/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
EOF
source ~/.zshrc

Linux (bash, appends to ~/.bashrc; use ~/.profile for login shells):

1
2
3
4
5
6
cat >> ~/.bashrc <<'EOF'
export ANDROID_HOME="$HOME/Android/Sdk"
export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"
export PATH="$JAVA_HOME/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
EOF
source ~/.bashrc

Windows (PowerShell, persists to your user environment):

1
2
3
4
setx ANDROID_HOME "$env:LOCALAPPDATA\Android\Sdk"
setx JAVA_HOME "C:\Program Files\Eclipse Adoptium\jdk-17"
# Then add these to your PATH (reopen the terminal afterwards):
#   %ANDROID_HOME%\platform-tools  %ANDROID_HOME%\emulator  %ANDROID_HOME%\cmdline-tools\latest\bin  %JAVA_HOME%\bin

Xcode vs. “Command Line Tools”: xcode-select --install installs only the standalone CLT (git, clang, etc.). That is not enough to build or deploy an iOS app, building for a device/simulator requires the full Xcode app plus its bundled SDKs. After installing Xcode, point the toolchain at it and accept the licenses (macOS steps are in Installing Flutter):

1
2
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -runFirstLaunch

iOS development is macOS-only, Xcode does not exist for Windows or Linux.


Node.js (only for Appium automation): the UI-automation harnesses (automation/scripts/appium_run_*.sh) and the automation/appium/ suite run on Node. Skip this if you’re only building/running the app by hand.

macOS (Homebrew):

1
brew install node

Debian/Ubuntu:

1
sudo apt-get install -y nodejs npm

Windows (winget):

1
winget install OpenJS.NodeJS.LTS

Run flutter doctor after setup; it flags anything missing for the platforms you plan to target.

Android device (physical)

  1. Enable Developer options: Settings β†’ About phone β†’ tap Build number 7Γ—.
  2. Settings β†’ System β†’ Developer options β†’ turn on USB debugging.
  3. Connect over USB and accept the “Allow USB debugging?” RSA prompt on the phone (tick “Always allow from this computer”). Re-accept if you switch cables or ports.
  4. Verify the host sees it: adb devices should list the serial as device (not unauthorized or offline). If it flaps, try a different cable/port and adb kill-server && adb start-server.

iOS device (physical)

  1. Developer Mode (iOS 16+): Settings β†’ Privacy & Security β†’ Developer Mode β†’ enable, then reboot. A signed build installs but won’t launch without this.
  2. Connect over USB and tap Trust This Computer on the iPhone.
  3. Signing: open ios/Runner.xcworkspace in Xcode β†’ Signing & Capabilities β†’ pick your Team (a free Apple ID works). Unsigned builds won’t install on a device; the Simulator needs no signing.
  4. Free Apple ID provisioning expires after 7 days, re-deploy weekly for ongoing testing. (A paid Apple Developer account lasts a year.)

Install fails with 0xe8008018 (“identity … no longer valid”)? The cert looks valid locally but Apple has revoked it server-side (common with free “Personal Team” certs). No CLI tool can re-mint it - you must let Xcode’s automatic signing regenerate the cert via Fix Issue. Full step-by-step is in the "⚠️ Install fails with 0xe8008018" box on the iOS install page.

Finding your Team ID. “Team” in Xcode maps to a 10-character Apple Team ID. Don’t hardcode someone else’s, use your own. Add your Apple ID under Xcode β†’ Settings β†’ Accounts first, then either pick the Team in Signing & Capabilities or set it via the xcconfig below. To look yours up from the CLI:

1
2
3
4
5
6
7
# From an installed provisioning profile (most reliable, shows TeamName too):
for p in ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles/*.mobileprovision; do
  security cms -D -i "$p" | plutil -extract TeamIdentifier xml1 -o - - | grep string
done

# Or list your code-signing identities (the name in quotes is your account):
security find-identity -v -p codesigning

If neither returns anything, you haven’t signed a build yet, add your account in Xcode and let it create a profile once, then re-run.


Set your Team via the local xcconfig (keeps IDs out of git). The Xcode project references DEVELOPMENT_TEAM = $(DEVELOPMENT_TEAM), resolved from a gitignored ios/Flutter/Signing.xcconfig. One-time setup:

1
2
cp ios/Flutter/Signing.xcconfig.example ios/Flutter/Signing.xcconfig
# then edit Signing.xcconfig and set DEVELOPMENT_TEAM to your own Team ID

Signing.xcconfig is gitignored, so each contributor uses their own team and nobody’s ID is committed. (Simulator builds need no signing, so this is only required for on-device builds.)


Seeing the stock “Flutter Demo Home Page” counter instead of DVMA? That means Xcode built a Flutter config pointing at the wrong project (a stale ios/Flutter/Generated.xcconfig, e.g. copied from a flutter create scaffold, sends the build to a different lib/main.dart). Regenerate the iOS build config for this repo, then clean-build:

1
2
3
flutter clean
flutter pub get
flutter build ios --config-only    # rewrites Generated.xcconfig for this path

Then in Xcode: Product β†’ Clean Build Folder (β‡§βŒ˜K) and Run. Always open the .xcworkspace, never the .xcodeproj.