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

View File

@@ -13,10 +13,23 @@ import 'core/chat/chat_notifier.dart';
import 'core/notifications/notification_service.dart';
import 'core/pairing/pairing_notifier.dart';
import 'core/theme/halo_theme.dart';
import 'firebase_options.dart';
import 'firebase/firebase_options_dev.dart';
import 'router.dart';
void main() async {
/// Shared app bootstrap, parameterised per build flavor.
///
/// The flavor entrypoints (`main_dev.dart`, `main_staging.dart`,
/// `main_prod.dart`) each call this with their environment's
/// [FirebaseOptions] and a [flavor] tag. The bare [main] below delegates to
/// dev so a plain `flutter run` (no `-t`) still launches the dev environment.
///
/// `flavor` is currently informational (kept on hand for future flavor-gated
/// behaviour / analytics tagging); the API base URL is supplied separately via
/// `--dart-define-from-file=env/<flavor>.json` (see BUILD_FLAVORS.md).
Future<void> bootstrap({
required FirebaseOptions firebaseOptions,
required String flavor,
}) async {
WidgetsFlutterBinding.ensureInitialized();
// Pre-warm flutter_secure_storage. The first call triggers AndroidX
@@ -26,7 +39,7 @@ void main() async {
// splash instead of paying it on the user's first interaction.
unawaited(TokenStorage().readRefreshToken());
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await Firebase.initializeApp(options: firebaseOptions);
final messaging = FirebaseMessaging.instance;
await messaging.requestPermission();
@@ -34,6 +47,16 @@ void main() async {
runApp(const ProviderScope(child: App()));
}
void main() async {
// Bare `flutter run` (no `-t lib/main_<flavor>.dart`) defaults to dev so
// local development works out of the box. Build-flavor APKs use the
// flavor-specific entrypoints instead.
await bootstrap(
firebaseOptions: DevFirebaseOptions.currentPlatform,
flavor: 'dev',
);
}
class App extends ConsumerStatefulWidget {
const App({super.key});