Complete guide for deploying Grex across Android (Play Store), iOS (App Store), and Web platforms.
The project supports three environments: development, staging, and production.
Create environment-specific configuration files:
.env.development
ENVIRONMENT=development
BASE_URL=http://localhost:3000
ENABLE_LOGGING=true
ENABLE_ANALYTICS=false
ENABLE_CRASH_REPORTING=false
ENABLE_PERFORMANCE_MONITORING=false
ENABLE_DEBUG_FEATURES=true
ENABLE_HTTP_LOGGING=true.env.staging
ENVIRONMENT=staging
BASE_URL=https://api-staging.example.com
ENABLE_LOGGING=true
ENABLE_ANALYTICS=true
ENABLE_CRASH_REPORTING=true
ENABLE_PERFORMANCE_MONITORING=true
ENABLE_DEBUG_FEATURES=false
ENABLE_HTTP_LOGGING=false.env.production
ENVIRONMENT=production
BASE_URL=https://api.example.com
ENABLE_LOGGING=false
ENABLE_ANALYTICS=true
ENABLE_CRASH_REPORTING=true
ENABLE_PERFORMANCE_MONITORING=true
ENABLE_DEBUG_FEATURES=false
ENABLE_HTTP_LOGGING=falseUpdate android/app/build.gradle.kts:
android {
// ... existing configuration ...
flavorDimensions += "environment"
productFlavors {
create("development") {
dimension = "environment"
applicationIdSuffix = ".dev"
versionNameSuffix = "-dev"
resValue("string", "app_name", "Grex Dev")
}
create("staging") {
dimension = "environment"
applicationIdSuffix = ".staging"
versionNameSuffix = "-staging"
resValue("string", "app_name", "Grex Staging")
}
create("production") {
dimension = "environment"
resValue("string", "app_name", "Grex")
}
}
}Create Xcode schemes for each environment:
- Open
ios/Runner.xcworkspacein Xcode - Product → Scheme → Manage Schemes
- Duplicate the "Runner" scheme for each environment:
Runner-DevelopmentRunner-StagingRunner-Production
For each scheme, configure:
- Build Configuration: Debug/Release
- Environment Variables: Add
ENVIRONMENT=development|staging|production
- Generate Keystore:
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload- Create
android/key.properties(add to.gitignore):
storePassword=<your-store-password>
keyPassword=<your-key-password>
keyAlias=upload
storeFile=<path-to-keystore>/upload-keystore.jks- Update
android/app/build.gradle.kts:
// Load keystore properties
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
// ... existing configuration ...
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}- Create
android/app/proguard-rules.pro:
# Flutter
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-
Create App ID in Apple Developer Portal
-
Create Provisioning Profiles:
- Development: For development builds
- Ad Hoc: For internal testing
- App Store: For App Store distribution
-
Configure Signing in Xcode:
- Open
ios/Runner.xcworkspace - Select Runner target → Signing & Capabilities
- Enable "Automatically manage signing"
- Select your Team
- Xcode will automatically manage certificates and profiles
- Open
-
Manual Signing (if needed):
- Disable "Automatically manage signing"
- Select provisioning profiles for each configuration
Development APK:
flutter build apk --flavor development --dart-define=ENVIRONMENT=developmentStaging APK:
flutter build apk --flavor staging --dart-define=ENVIRONMENT=staging --dart-define=BASE_URL=https://api-staging.example.comProduction APK:
flutter build apk --flavor production --release --dart-define=ENVIRONMENT=production --dart-define=BASE_URL=https://api.example.comProduction App Bundle (for Play Store):
flutter build appbundle --flavor production --release --dart-define=ENVIRONMENT=production --dart-define=BASE_URL=https://api.example.comDevelopment:
flutter build ios --flavor development --dart-define=ENVIRONMENT=development --no-codesignStaging:
flutter build ios --flavor staging --release --dart-define=ENVIRONMENT=staging --dart-define=BASE_URL=https://api-staging.example.comProduction (for App Store):
flutter build ipa --flavor production --release --dart-define=ENVIRONMENT=production --dart-define=BASE_URL=https://api.example.comDevelopment:
flutter build web --dart-define=ENVIRONMENT=development --dart-define=BASE_URL=http://localhost:3000Staging:
flutter build web --release --dart-define=ENVIRONMENT=staging --dart-define=BASE_URL=https://api-staging.example.comProduction:
flutter build web --release --dart-define=ENVIRONMENT=production --dart-define=BASE_URL=https://api.example.comThe project includes automated CI/CD workflows for:
- Continuous Integration: Run tests on every push/PR
- Automated Builds: Build artifacts for all platforms
- Automated Deployment: Deploy to stores/hosting
All workflows are located in .github/workflows/ and are disabled by default (all triggers commented out) to prevent automatic execution in template repositories.
Available Workflows:
ci.yml- Continuous Integration (format, analyze, test, build)test.yml- Dedicated test workflow with coveragecoverage.yml- Coverage analysis and reportingdeploy-android.yml- Android deployment to Play Storedeploy-ios.yml- iOS deployment to App Storedeploy-web.yml- Web deployment to hosting platforms
Configuration:
-
Enable workflows - Uncomment trigger configuration in workflow files:
on: # Uncomment the triggers you want to enable: push: branches: [ main, develop ] # Uncomment and configure pull_request: branches: [ main, develop ] # Uncomment and configure # workflow_dispatch is enabled by default for manual triggers
Note: All
pushandpull_requesttriggers are commented out by default. Workflows will only run viaworkflow_dispatch(manual trigger) until you uncomment the triggers. -
Configure secrets - Add required secrets in GitHub Settings → Secrets and variables → Actions:
For Testing:
CODECOV_TOKEN(for private repos)
For Android Deployment:
ANDROID_KEYSTORE_BASE64ANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORDGOOGLE_PLAY_SERVICE_ACCOUNT_JSON
For iOS Deployment:
APPLE_TEAM_IDAPP_STORE_CONNECT_API_KEY_IDAPP_STORE_CONNECT_ISSUER_IDAPP_STORE_CONNECT_API_KEY_BASE64
For Web Deployment:
FIREBASE_PROJECT_IDFIREBASE_SERVICE_ACCOUNT_KEY
-
Codecov Setup (for private repos):
- Sign up at https://codecov.io
- Add repository and get token
- Add
CODECOV_TOKENto GitHub Secrets - Workflows are pre-configured for private repos
The app version is defined in pubspec.yaml:
version: 1.0.0+1Format: MAJOR.MINOR.PATCH+BUILD_NUMBER
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes
- BUILD_NUMBER: Incremental build number (required by stores)
Use the provided script:
./scripts/bump_version.sh [major|minor|patch] [build_number]Examples:
# Bump patch version
./scripts/bump_version.sh patch
# Bump minor version
./scripts/bump_version.sh minor
# Bump major version
./scripts/bump_version.sh major
# Set specific build number
./scripts/bump_version.sh patch 42Changelogs are automatically generated from git commits. Use conventional commit format:
feat: Add new feature
fix: Fix bug
docs: Update documentation
style: Code style changes
refactor: Code refactoring
test: Add tests
chore: Maintenance tasks
Generate changelog:
./scripts/generate_changelog.sh- Update version in
pubspec.yaml - Update
CHANGELOG.md - Run all tests:
flutter test - Run linting:
flutter analyze - Test on all platforms (Android, iOS, Web)
- Update dependencies:
flutter pub upgrade - Verify environment variables
- Create release branch:
git checkout -b release/v1.0.0 - Tag release:
git tag -a v1.0.0 -m "Release v1.0.0"
- APK:
build/app/outputs/flutter-apk/app-production-release.apk - App Bundle:
build/app/outputs/bundle/productionRelease/app-production-release.aab
- IPA:
build/ios/ipa/Runner.ipa
- Web Build:
build/web/
-
Create Release in Play Console:
- Go to Google Play Console
- Select your app → Production → Create new release
- Upload the
.aabfile
-
Release Notes:
- Add release notes for users
- Include what's new in this version
-
Review and Rollout:
- Review all information
- Start rollout (staged or full)
-
Archive in Xcode:
# Or use CI/CD to build IPA flutter build ipa --release -
Upload via Transporter or Xcode:
- Open Xcode → Window → Organizer
- Select archive → Distribute App
- Choose App Store Connect
- Follow the wizard
-
Submit for Review:
- Go to App Store Connect
- Select your app → Prepare for Submission
- Fill in all required information
- Submit for review
Firebase Hosting:
firebase deploy --only hostingOther Hosting Providers:
- Upload
build/web/directory to your hosting service - Configure routing for SPA (all routes →
index.html)
- Add Dependencies to
pubspec.yaml:
dependencies:
firebase_core: ^3.0.0
firebase_crashlytics: ^4.0.0- Initialize in
main.dart:
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// Initialize Crashlytics
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
return true;
};
// ... rest of initialization
}- Configure Firebase Projects:
- Create Firebase projects for each environment
- Add
google-services.json(Android) andGoogleService-Info.plist(iOS) - Place in appropriate flavor directories
Firebase Analytics:
import 'package:firebase_analytics/firebase_analytics.dart';
final analytics = FirebaseAnalytics.instance;
// Track events
await analytics.logEvent(
name: 'button_click',
parameters: {'button_name': 'submit'},
);Custom Analytics:
// In your analytics service
if (AppConfig.enableAnalytics) {
await analyticsService.trackEvent('event_name', parameters);
}Firebase Performance:
import 'package:firebase_performance/firebase_performance.dart';
final trace = FirebasePerformance.instance.newTrace('api_call');
await trace.start();
// Your API call
await apiService.fetchData();
await trace.stop();See android-deployment.md for detailed Android-specific deployment instructions.
See ios-deployment.md for detailed iOS-specific deployment instructions.
See web-deployment.md for detailed Web-specific deployment instructions.
Build Failures:
- Check Flutter version:
flutter --version - Clean build:
flutter clean && flutter pub get - Check environment variables are set correctly
Code Signing Issues:
- Verify keystore/certificate files exist
- Check passwords are correct
- Ensure certificates haven't expired
CI/CD Failures:
- Verify all secrets are set in GitHub (see Required Secrets section above)
- Check workflow logs for specific errors
- Ensure runner has required tools installed
- Workflows are disabled by default - Uncomment
pushandpull_requesttriggers in workflow files to enable automatic runs - Verify GitHub Actions is enabled in repository settings
- Check Troubleshooting Guide
- Review workflow logs in GitHub Actions tab
- Check platform-specific deployment guides
- Set up code signing for Android and iOS
- Configure GitHub Actions secrets (see Required Secrets section above)
- Set up Firebase projects for each environment
- Test build process locally
- Create first release