# Build Flavors — client_app (Android + iOS identity) Three build flavors map each environment to its own app ID, backend URL, app display name, and Firebase config. This lets dev / staging / prod builds sit **side-by-side** on one device (distinct package / bundle IDs). > Scope: **Android flavors + Dart + env files are fully wired.** On iOS, only > the **bundle identity** is set (see the iOS section below); the iOS **Xcode > build-configuration + scheme wiring** (per-flavor bundle suffixing, > per-scheme `GoogleService-Info.plist`) is a separate follow-up that needs a > Mac and is NOT done here. ## Flavor table | Flavor | applicationId | App name | API_BASE_URL | |----------|-------------------------|--------------------|-------------------------------------------| | dev | `com.mybestie.dev` | HaloBestie Dev | `http://192.168.88.247:3000` | | staging | `com.mybestie.staging` | HaloBestie Staging | `https://staging-api.halobestie.com` ⚠️\* | | prod | `com.mybestie` | HaloBestie | `https://api.halobestie.com` | \* staging URL is a **PLACEHOLDER** — confirm the real staging host and update `env/staging.json` (see the `_TODO` key there). The `applicationId` is set in `android/app/build.gradle.kts` via `applicationIdSuffix` (dev/staging) on top of the base `com.mybestie`; prod has no suffix. The app name comes from a per-flavor `resValue("string","app_name",…)` consumed by `android:label="@string/app_name"` in `AndroidManifest.xml`. ## iOS bundle identity — DIFFERENT base from Android ⚠️ The customer app already exists on the App Store under bundle ID **`com.asc.hallobestie`** (published by a prior vendor — note the `asc` prefix and `hallobestie` spelling). **Decision: v2 ships as an _update_ to that existing listing**, so the iOS **prod** bundle ID must stay `com.asc.hallobestie` (you cannot change a published app's bundle ID without it becoming a new app). The iOS flavors therefore suffix **that** base, NOT the Android `com.mybestie` base: | Flavor | Android `applicationId` | iOS bundle ID | |---------|-------------------------|--------------------------------| | dev | `com.mybestie.dev` | `com.asc.hallobestie.dev` | | staging | `com.mybestie.staging` | `com.asc.hallobestie.staging` | | prod | `com.mybestie` | `com.asc.hallobestie` | A bundle ID differing across platforms is normal. What's done so far on iOS: - `ios/Runner.xcodeproj/project.pbxproj` — base `PRODUCT_BUNDLE_IDENTIFIER` set to `com.asc.hallobestie` (+ `com.asc.hallobestie.RunnerTests`). Once iOS schemes are wired, dev/staging suffix off this base. - `firebase_options_{dev,staging,prod}.dart` — `iosBundleId` set to the table above. dev's iOS appId/apiKey are now PLACEHOLDERs (the old `com.mybestie` iOS app no longer matches) — register `com.asc.hallobestie.dev` in the dev project and paste the values. - `ios/Runner/Info.plist` — `CFBundleURLName` aligned to `com.asc.hallobestie` (the `halobestie://` deeplink scheme itself is unchanged). Still TODO on iOS (the deferred Mac/Xcode work): - The existing `ios/Runner/GoogleService-Info.plist` still lists `com.mybestie` — replace per flavor once the iOS apps are registered. - Xcode build configs + schemes to actually apply the `.dev`/`.staging` bundle suffixes per build, with a copy-script selecting the right `GoogleService-Info.plist`. - You need access to the **`asc` Apple Developer account** + its signing certs/provisioning profiles to release the prod update. > **mitra app is unaffected** — it's a brand-new app (`com.mybestie.mitra` + > suffixes) on both platforms; only the customer iOS app inherits the legacy > `com.asc.hallobestie` identity. ## How the pieces fit | Concern | dev | staging | prod | |----------------------|---------------------------------------|--------------------------------------------|-----------------------------------------| | Dart entrypoint | `lib/main_dev.dart` | `lib/main_staging.dart` | `lib/main_prod.dart` | | Firebase Dart opts | `lib/firebase/firebase_options_dev.dart` | `…_staging.dart` (PLACEHOLDER) | `…_prod.dart` (PLACEHOLDER) | | Android Firebase cfg | `android/app/src/dev/google-services.json` | `android/app/src/staging/…` (MISSING) | `android/app/src/prod/…` (MISSING) | | Env / dart-define | `env/dev.json` | `env/staging.json` | `env/prod.json` | A bare `flutter run` (no `-t`) still works and defaults to **dev**: `lib/main.dart`'s `main()` delegates to `bootstrap(flavor: 'dev', …)`. ## Run / build commands Each command needs three things: `--flavor `, `-t lib/main_.dart`, and `--dart-define-from-file=env/.json`. ### Run on a connected device/emulator ```bash # dev flutter run --flavor dev -t lib/main_dev.dart --dart-define-from-file=env/dev.json # staging flutter run --flavor staging -t lib/main_staging.dart --dart-define-from-file=env/staging.json # prod flutter run --flavor prod -t lib/main_prod.dart --dart-define-from-file=env/prod.json ``` ### Build a release APK ```bash # dev flutter build apk --flavor dev -t lib/main_dev.dart --dart-define-from-file=env/dev.json # staging flutter build apk --flavor staging -t lib/main_staging.dart --dart-define-from-file=env/staging.json # prod flutter build apk --flavor prod -t lib/main_prod.dart --dart-define-from-file=env/prod.json ``` ### Build an App Bundle (Play release) ```bash flutter build appbundle --flavor prod -t lib/main_prod.dart --dart-define-from-file=env/prod.json ``` Output APKs land in `build/app/outputs/flutter-apk/app--release.apk`. ## ⚠️ CRITICAL warnings ### (a) Every build/install/run command now needs `--flavor` Once product flavors exist, a bare `flutter build apk` / `flutter install` / `flutter run` **with no `--flavor`** FAILS (Gradle can't pick a flavor). All build, install, and run invocations — CI scripts, local muscle memory, IDE launch configs — must pass `--flavor ` plus the matching `-t lib/main_.dart` and `--dart-define-from-file=env/.json`. ### (b) The dev applicationId is now `com.mybestie.dev`, not `com.mybestie` Anything that references the package name by string must be updated for dev: - **GA4 DebugView:** ```bash adb shell setprop debug.firebase.analytics.app com.mybestie.dev ``` - **adb commands** (force-stop, pm clear, am start, grant, etc.): ```bash adb shell am force-stop com.mybestie.dev adb shell pm clear com.mybestie.dev ``` - **Maestro flows:** any `appId: com.mybestie` must become `appId: com.mybestie.dev` for the dev build. Check `.maestro/` configs and per-flow `appId` headers. Staging uses `com.mybestie.staging`; prod stays `com.mybestie`. ## Firebase config — STATUS: configured ✅ (2026-06-04) All apps are registered and config files are in place for **both platforms**, across 3 projects (one Firebase project per env): | Env | Firebase project | Android applicationId | iOS bundle ID | |---------|------------------------|------------------------|-------------------------------| | dev | `halobestie-clone-dev` | `com.mybestie.dev` | `com.asc.hallobestie.dev` | | staging | `my-bestie-876ec` | `com.mybestie.staging` | `com.asc.hallobestie.staging` | | prod | `my-bestie-production` | `com.mybestie` | `com.asc.hallobestie` | In place and verified: - `android/app/src//google-services.json` — all 3, each containing a client matching the flavor `applicationId`. - `ios/config//GoogleService-Info.plist` — all 3, bundle IDs verified. - `lib/firebase/firebase_options_{dev,staging,prod}.dart` — real android + iOS values (regenerated from the native files above; no placeholders left). ### Regenerating after any ID / key change Either re-run flutterfire per flavor, or re-download the native files and re-extract. Project IDs: ```bash flutterfire configure --project=halobestie-clone-dev --out=lib/firebase/firebase_options_dev.dart flutterfire configure --project=my-bestie-876ec --out=lib/firebase/firebase_options_staging.dart flutterfire configure --project=my-bestie-production --out=lib/firebase/firebase_options_prod.dart ``` ### Still TODO — iOS only (Mac/Xcode) - [ ] iOS **Xcode schemes + build-phase copy script** to apply per-flavor bundle suffixes and select the right `GoogleService-Info.plist` at build time. Until then, iOS bundles only `ios/Runner/GoogleService-Info.plist`. - [ ] Replace `ios/Runner/GoogleService-Info.plist` (still the legacy `com.mybestie` dev file) — or let the copy script overwrite it per build. - [ ] Prod iOS release needs the **`asc` Apple Developer account** + signing. ## Files in this setup - `android/app/build.gradle.kts` — `flavorDimensions` + `productFlavors` - `android/app/src/main/AndroidManifest.xml` — `android:label="@string/app_name"` - `lib/main.dart` — `bootstrap()` + dev-default `main()` - `lib/main_dev.dart` / `lib/main_staging.dart` / `lib/main_prod.dart` - `lib/firebase/firebase_options_{dev,staging,prod}.dart` - `env/{dev,staging,prod}.json` - `android/app/src/{dev,staging,prod}/` Firebase config source sets