feat(build): add dev/staging/prod flavors for client_app + mitra_app

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>
This commit is contained in:
2026-06-04 22:21:50 +08:00
parent 76d74aa7b5
commit 22743c81e1
53 changed files with 1891 additions and 178 deletions

121
mitra_app/BUILD_FLAVORS.md Normal file
View File

@@ -0,0 +1,121 @@
# Build Flavors — mitra_app (Android)
The mitra_app has three Android build flavors: **dev**, **staging**, **prod**.
Each has its own `applicationId`, backend URL, app display name, Dart entrypoint,
Firebase Dart options, and `google-services.json` source set — so all three can
be installed side-by-side on one device.
> Scope note: this is **Android + Dart + env-files** only. iOS Xcode schemes are
> a separate follow-up and are NOT set up yet.
## Flavor matrix
| Flavor | applicationId | API_BASE_URL | App name | Entrypoint | env file |
|---------|-----------------------------|-------------------------------------------|---------------------------|-----------------------|-------------------|
| dev | `com.mybestie.mitra.dev` | `http://192.168.88.247:3000` | Mitra HaloBestie Dev | `lib/main_dev.dart` | `env/dev.json` |
| staging | `com.mybestie.mitra.staging`| `https://staging-api.halobestie.com` ⚠️ | Mitra HaloBestie Staging | `lib/main_staging.dart`| `env/staging.json`|
| prod | `com.mybestie.mitra` | `https://api.halobestie.com` | Mitra HaloBestie | `lib/main_prod.dart` | `env/prod.json` |
⚠️ The staging `API_BASE_URL` is a **placeholder** — confirm the real staging
host and update `env/staging.json` + `lib/firebase/firebase_options_staging.dart`.
The `applicationId` suffix is applied in `android/app/build.gradle.kts`
(`applicationIdSuffix = ".dev"` / `".staging"`; prod has none). The app name is
emitted per flavor via `resValue("string", "app_name", "...")` and read by
`android/app/src/main/AndroidManifest.xml` through `android:label="@string/app_name"`.
## Build / run commands
Every command MUST pass `--flavor`, a matching `-t` entrypoint, and
`--dart-define-from-file` for the env. Examples:
### Run (debug, on a device/emulator)
```bash
flutter run --flavor dev -t lib/main_dev.dart --dart-define-from-file=env/dev.json
flutter run --flavor staging -t lib/main_staging.dart --dart-define-from-file=env/staging.json
flutter run --flavor prod -t lib/main_prod.dart --dart-define-from-file=env/prod.json
```
### Build APK
```bash
flutter build apk --flavor dev -t lib/main_dev.dart --dart-define-from-file=env/dev.json
flutter build apk --flavor staging -t lib/main_staging.dart --dart-define-from-file=env/staging.json
flutter build apk --flavor prod -t lib/main_prod.dart --dart-define-from-file=env/prod.json
```
### Build App Bundle (Play Store)
```bash
flutter build appbundle --flavor prod -t lib/main_prod.dart --dart-define-from-file=env/prod.json
```
A bare `flutter run` (no `-t`) still works — `lib/main.dart` delegates to the
dev bootstrap — but it builds with no flavor selected on Android, so prefer the
explicit commands above.
## ⚠️ CRITICAL warnings
1. **`--flavor` is now mandatory for builds.** Once product flavors exist, a
bare `flutter build apk` (without `--flavor`) FAILS with a Gradle error
(no default flavor). Every build/run command must specify `--flavor` and the
matching `-t lib/main_<flavor>.dart` entrypoint.
2. **The dev applicationId changed to `com.mybestie.mitra.dev`.** Any tooling
that references the old package id must be updated when running the dev
flavor:
- `adb` commands: `adb shell pm clear com.mybestie.mitra.dev`,
`adb shell am start ... com.mybestie.mitra.dev/...`, etc.
- Maestro flows: `appId: com.mybestie.mitra.dev`.
- Any deeplink / FCM tooling keyed on the package name.
Prod keeps `com.mybestie.mitra`; staging is `com.mybestie.mitra.staging`.
## Firebase config — STATUS: configured ✅ (2026-06-04)
Firebase init is **Dart-side** (`Firebase.initializeApp(options:)` in
`lib/bootstrap.dart`), driven by the per-flavor
`lib/firebase/firebase_options_<flavor>.dart`. The mitra app is a **brand-new
app** on both platforms (no legacy App Store identity), so the iOS bundle base
is `com.mybestie.mitra` — unlike the customer app, which inherits
`com.asc.hallobestie`.
All apps registered + config in place across 3 projects (one per env):
| Env | Firebase project | Android applicationId | iOS bundle ID |
|---------|------------------------|-------------------------------|------------------------------|
| dev | `halobestie-clone-dev` | `com.mybestie.mitra.dev` | `com.mybestie.mitra.dev` |
| staging | `my-bestie-876ec` | `com.mybestie.mitra.staging` | `com.mybestie.mitra.staging` |
| prod | `my-bestie-production` | `com.mybestie.mitra` | `com.mybestie.mitra` |
In place and verified:
- `android/app/src/<flavor>/google-services.json` — all 3, client matches 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, no placeholders.
### Regenerating after any ID / key change
```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 select the right
`GoogleService-Info.plist` per flavor (until then iOS bundles only
`ios/Runner/GoogleService-Info.plist`). See `ios/config/README.md`.
> The Google Services Gradle plugin is **not** applied in this app — Android
> Firebase init is Dart-side. The `src/<flavor>/google-services.json` files are
> laid out for if/when that plugin is added.
## File map
| File | Purpose |
|---|---|
| `android/app/build.gradle.kts` | `flavorDimensions "env"` + `productFlavors` (id suffix + app_name) |
| `android/app/src/main/AndroidManifest.xml` | `android:label="@string/app_name"` |
| `android/app/src/dev/google-services.json` | dev Firebase config |
| `android/app/src/{staging,prod}/google-services.json.README` | placeholders — drop real json here |
| `lib/bootstrap.dart` | shared `bootstrap()` + `App` widget |
| `lib/main.dart` | bare entrypoint → delegates to dev |
| `lib/main_{dev,staging,prod}.dart` | per-flavor entrypoints |
| `lib/firebase/firebase_options_{dev,staging,prod}.dart` | per-flavor Dart Firebase options |
| `env/{dev,staging,prod}.json` | dart-define values (`API_BASE_URL`, `FLAVOR`) |