From 7303ab82b228e252dc9e831639bce13ef2f021cb Mon Sep 17 00:00:00 2001 From: Bretton Date: Fri, 28 Nov 2025 22:38:13 +0000 Subject: [PATCH] feat: add Flutter flavors for dev/prod builds and update app icon - Add Android productFlavors (dev: social.coves.dev, prod: social.coves) - Create iOS flavor xcconfig files for future scheme setup - Update EnvironmentConfig to support flavor-based environment detection - Add VSCode launch configurations for easy flavor switching - Update app icon to lil_dude mascot with proper adaptive icon padding Dev flavor points to local server, prod flavor points to coves.social. Both apps can be installed side-by-side on the same device. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .vscode/launch.json | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ android/app/build.gradle.kts | 17 ++++++++++++++++- android/app/src/main/AndroidManifest.xml | 2 +- android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png | 0 android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png | 0 android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png | 0 android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png | 0 android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png | 0 android/app/src/main/res/mipmap-hdpi/ic_launcher.png | 0 android/app/src/main/res/mipmap-mdpi/ic_launcher.png | 0 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png | 0 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png | 0 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | 0 assets/logo/lil_dude.png | 0 assets/logo/lil_dude_padded.png | 0 ios/Flutter/Dev-Debug.xcconfig | 8 ++++++++ ios/Flutter/Dev-Release.xcconfig | 8 ++++++++ ios/Flutter/Prod-Debug.xcconfig | 8 ++++++++ ios/Flutter/Prod-Release.xcconfig | 8 ++++++++ ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png | 0 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png | 0 ios/Runner/Info.plist | 4 ++-- lib/config/environment_config.dart | 61 +++++++++++++++++++++++++++++++++++++++++++++++-------------- pubspec.yaml | 4 ++-- 43 file(s) changed, 159 insertion(s)(+), 20 deletion(s)(-) diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,59 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Dev (Local Server)", + "request": "launch", + "type": "dart", + "flutterMode": "debug", + "args": [ + "--flavor", + "dev", + "--dart-define=FLUTTER_FLAVOR=dev" + ] + }, + { + "name": "Dev (Local Server) - Release", + "request": "launch", + "type": "dart", + "flutterMode": "release", + "args": [ + "--flavor", + "dev", + "--dart-define=FLUTTER_FLAVOR=dev" + ] + }, + { + "name": "Prod (Production Server)", + "request": "launch", + "type": "dart", + "flutterMode": "debug", + "args": [ + "--flavor", + "prod", + "--dart-define=FLUTTER_FLAVOR=prod" + ] + }, + { + "name": "Prod (Production Server) - Release", + "request": "launch", + "type": "dart", + "flutterMode": "release", + "args": [ + "--flavor", + "prod", + "--dart-define=FLUTTER_FLAVOR=prod" + ] + }, + { + "name": "Legacy: Local (no flavor)", + "request": "launch", + "type": "dart", + "flutterMode": "debug", + "args": [ + "--dart-define=ENVIRONMENT=local" + ] + } + ], + "compounds": [] +} diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -20,7 +20,7 @@ jvmTarget = JavaVersion.VERSION_11.toString() } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + // Base application ID - flavors will add suffixes applicationId = "social.coves" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. @@ -28,6 +28,21 @@ minSdk = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName + } + + // Flutter flavors for side-by-side installation + flavorDimensions += "environment" + productFlavors { + create("prod") { + dimension = "environment" + applicationIdSuffix = "" + resValue("string", "app_name", "Coves") + } + create("dev") { + dimension = "environment" + applicationIdSuffix = ".dev" + resValue("string", "app_name", "Coves Dev") + } } buildTypes { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Coves + $(DISPLAY_NAME) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -13,7 +13,7 @@ $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName - coves_flutter + $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString diff --git a/lib/config/environment_config.dart b/lib/config/environment_config.dart --- a/lib/config/environment_config.dart +++ b/lib/config/environment_config.dart @@ -1,10 +1,13 @@ /// Environment Configuration for Coves Mobile /// /// Supports multiple environments: -/// - Production: Real Bluesky infrastructure -/// - Local: Local PDS + PLC for development/testing +/// - Production: Real Bluesky infrastructure (prod flavor) +/// - Local: Local PDS + PLC for development/testing (dev flavor) /// -/// Set via ENVIRONMENT environment variable or flutter run --dart-define +/// Environment is determined by (in priority order): +/// 1. --dart-define=ENVIRONMENT=local/production (explicit override) +/// 2. Flutter flavor (dev -> local, prod -> production) +/// 3. Default: production enum Environment { production, local } class EnvironmentConfig { @@ -20,10 +23,10 @@ final String handleResolverUrl; final String plcDirectoryUrl; /// Production configuration (default) - /// Uses real Bluesky infrastructure + /// Uses Coves production server with public atproto infrastructure static const production = EnvironmentConfig( environment: Environment.production, - apiUrl: 'https://coves.social', // TODO: Update when production is live + apiUrl: 'https://coves.social', handleResolverUrl: 'https://bsky.social/xrpc/com.atproto.identity.resolveHandle', plcDirectoryUrl: 'https://plc.directory', @@ -46,21 +49,51 @@ 'http://localhost:3001/xrpc/com.atproto.identity.resolveHandle', plcDirectoryUrl: 'http://localhost:3002', ); + /// Flutter flavor passed via --flavor flag + /// This is set automatically by Flutter build system + static const String _flavor = String.fromEnvironment('FLUTTER_FLAVOR'); + + /// Explicit environment override via --dart-define=ENVIRONMENT=local + static const String _envOverride = String.fromEnvironment('ENVIRONMENT'); + /// Get current environment based on build configuration + /// + /// Priority: + /// 1. Explicit --dart-define=ENVIRONMENT=local/production + /// 2. Flavor: dev -> local, prod -> production + /// 3. Default: production static EnvironmentConfig get current { - // Read from --dart-define=ENVIRONMENT=local - const envString = String.fromEnvironment( - 'ENVIRONMENT', - defaultValue: 'production', - ); + // Priority 1: Explicit environment override + if (_envOverride.isNotEmpty) { + switch (_envOverride) { + case 'local': + return local; + case 'production': + return production; + } + } - switch (envString) { - case 'local': + // Priority 2: Flavor-based environment + switch (_flavor) { + case 'dev': return local; - case 'production': - default: + case 'prod': return production; } + + // Default: production + return production; + } + + /// Get the current flavor name for display purposes + static String get flavorName { + if (_flavor.isNotEmpty) { + return _flavor; + } + if (_envOverride == 'local') { + return 'dev'; + } + return 'prod'; } bool get isProduction => environment == Environment.production; diff --git a/pubspec.yaml b/pubspec.yaml --- a/pubspec.yaml +++ b/pubspec.yaml @@ -73,9 +73,9 @@ # Launcher icon configuration flutter_launcher_icons: android: true ios: true - image_path: "assets/logo/app-icon.png" + image_path: "assets/logo/lil_dude_padded.png" adaptive_icon_background: "#0B0F14" # Your app's dark background color - adaptive_icon_foreground: "assets/logo/app-icon-foreground.png" # Smaller version with padding + adaptive_icon_foreground: "assets/logo/lil_dude_padded.png" # Lil dude with safe zone padding remove_alpha_ios: true # Required for App Store submission # For information on the generic Dart part of this file, see the -- tangled.sh