Android product flavors (.dev/.staging suffixes, prod clean) + per-flavor
Dart entrypoints, dart-define env files, and per-flavor Firebase config for
both platforms across 3 projects (halobestie-clone-dev / my-bestie-876ec /
my-bestie-production).
- Android: flavorDimensions("env") + productFlavors; @string/app_name label;
per-flavor src/<flavor>/google-services.json (clients verified to match each
applicationId).
- iOS: customer app re-based to the EXISTING App Store identity
com.asc.hallobestie (dev/staging suffix it; ships as an update to the live
app). mitra is a new app (com.mybestie.mitra). Per-flavor plists staged in
ios/config/<flavor>/; Xcode scheme wiring deferred (Mac follow-up).
- firebase_options_{dev,staging,prod}.dart filled with real android + iOS
values (regenerated from the native config files).
- BUILD_FLAVORS.md per app documents flavor table, build commands, iOS
identity decision, and the remaining iOS Xcode steps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
185 lines
9.4 KiB
Markdown
185 lines
9.4 KiB
Markdown
# 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 <f>`, `-t lib/main_<f>.dart`, and
|
|
`--dart-define-from-file=env/<f>.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-<flavor>-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 <dev|staging|prod>` plus the matching
|
|
`-t lib/main_<flavor>.dart` and `--dart-define-from-file=env/<flavor>.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/<flavor>/google-services.json` — all 3, each containing a
|
|
client matching the flavor `applicationId`.
|
|
- `ios/config/<flavor>/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
|