Skip to content

Build Process

This guide covers the complete build process for deploying Lumo to production, including optimization, signing, and automation.

lib/core/config/app_config.dart
class AppConfig {
static const bool isProduction = bool.fromEnvironment(
'PRODUCTION',
defaultValue: false,
);
static const bool enableAnalytics = bool.fromEnvironment(
'ENABLE_ANALYTICS',
defaultValue: isProduction,
);
static const bool enableCrashReporting = bool.fromEnvironment(
'ENABLE_CRASH_REPORTING',
defaultValue: isProduction,
);
static const String apiBaseUrl = String.fromEnvironment(
'API_BASE_URL',
defaultValue: isProduction
? 'https://api.yourapp.com'
: 'https://api-dev.yourapp.com',
);
static const String firebaseProjectId = String.fromEnvironment(
'FIREBASE_PROJECT_ID',
defaultValue: 'your-app-production',
);
}
lib/main.dart
import 'package:flutter/foundation.dart';
import 'core/config/app_config.dart';
void main() {
// Configure based on build mode
if (kDebugMode) {
// Debug configuration
setupDebugMode();
} else {
// Production configuration
setupProductionMode();
}
runApp(const MyApp());
}
void setupProductionMode() {
// Disable debug prints
debugPrint = (String? message, {int? wrapWidth}) {};
// Enable crash reporting
if (AppConfig.enableCrashReporting) {
FlutterError.onError = (details) {
// Send to crash reporting service
FirebaseCrashlytics.instance.recordFlutterError(details);
};
}
}
void setupDebugMode() {
// Keep debug prints enabled
// Disable analytics in debug
}
Terminal window
# Create release keystore (keep secure!)
keytool -genkey -v \
-keystore android/app/release-keystore.jks \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-alias release-key
# Example values:
# First and Last Name: Your App Name
# Organizational Unit: Mobile Development
# Organization: Your Company
# City: Your City
# State: Your State/Province
# Country Code: US
# Password: Use a strong password!

Create android/key.properties:

storePassword=your_store_password
keyPassword=your_key_password
keyAlias=release-key
storeFile=release-keystore.jks

Update android/app/build.gradle.kts:

import java.util.Properties
import java.io.FileInputStream
// Load keystore properties
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
namespace = "com.yourcompany.yourapp"
compileSdk = 34
defaultConfig {
applicationId = "com.yourcompany.yourapp"
minSdk = 23
targetSdk = 34
versionCode = flutter.versionCode
versionName = flutter.versionName
// ProGuard configuration for release builds
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
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 {
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName("release")
// Add build config fields
buildConfigField("boolean", "ENABLE_LOGGING", "false")
buildConfigField("String", "BUILD_TIME", "\"${System.currentTimeMillis()}\"")
}
debug {
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
buildConfigField("boolean", "ENABLE_LOGGING", "true")
}
}
}

Create android/app/proguard-rules.pro:

# Keep Flutter wrapper
-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.** { *; }
# Keep Firebase
-keep class com.google.firebase.** { *; }
-keep class com.google.android.gms.** { *; }
# Keep Realm
-keep class io.realm.** { *; }
-dontwarn io.realm.**
# Keep crypto libraries
-keep class javax.crypto.** { *; }
-keep class java.security.** { *; }
# Keep app-specific classes
-keep class com.yourcompany.yourapp.** { *; }
# Remove logging in release
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
public static *** i(...);
}
Terminal window
# Clean previous builds
flutter clean
flutter pub get
# Build optimized app bundle
flutter build appbundle \
--release \
--obfuscate \
--split-debug-info=build/debug-info \
--dart-define=PRODUCTION=true \
--dart-define=ENABLE_ANALYTICS=true \
--dart-define=FIREBASE_PROJECT_ID=your-production-project
# Output: build/app/outputs/bundle/release/app-release.aab
Terminal window
# Build release APK
flutter build apk \
--release \
--obfuscate \
--split-debug-info=build/debug-info \
--dart-define=PRODUCTION=true
# Build split APKs by ABI (smaller downloads)
flutter build apk \
--release \
--split-per-abi \
--obfuscate \
--split-debug-info=build/debug-info
Terminal window
# Check APK/AAB contents
aapt dump badging build/app/outputs/bundle/release/app-release.aab
# Test installation
adb install build/app/outputs/flutter-apk/app-release.apk
# Check app size
ls -lh build/app/outputs/flutter-apk/app-release.apk
  1. Open ios/Runner.xcworkspace in Xcode
  2. Select “Runner” project
  3. In “Signing & Capabilities”:
    • Team: Select your Apple Developer team
    • Bundle Identifier: com.yourcompany.yourapp
    • Provisioning Profile: App Store (automatic)
ios/Flutter/Release.xcconfig
#include "Generated.xcconfig"
// Production configuration
FLUTTER_BUILD_MODE=release
DART_DEFINES=PRODUCTION=true,ENABLE_ANALYTICS=true,FIREBASE_PROJECT_ID=your-production-project
// Optimization
ENABLE_BITCODE=NO
STRIP_INSTALLED_PRODUCT=YES
COPY_PHASE_STRIP=YES
DEBUG_INFORMATION_FORMAT=dwarf-with-dsym
// Security
GCC_GENERATE_DEBUGGING_SYMBOLS=YES
GCC_OPTIMIZATION_LEVEL=s
SWIFT_OPTIMIZATION_LEVEL=-O
Terminal window
# Clean previous builds
flutter clean
flutter pub get
# Build iOS release
flutter build ios \
--release \
--obfuscate \
--split-debug-info=build/debug-info \
--dart-define=PRODUCTION=true \
--dart-define=ENABLE_ANALYTICS=true
  1. Open ios/Runner.xcworkspace
  2. Select “Any iOS Device (arm64)” or connected device
  3. Product → Archive
  4. When archive completes, select “Distribute App”
  5. Choose “App Store Connect”
  6. Upload to TestFlight/App Store
.github/workflows/ios-build.yml
name: iOS Build
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.8.1'
- name: Install dependencies
run: flutter pub get
- name: Build iOS
run: |
flutter build ios \
--release \
--obfuscate \
--split-debug-info=build/debug-info \
--dart-define=PRODUCTION=true
- name: Archive with Xcode
run: |
cd ios
xcodebuild -workspace Runner.xcworkspace \
-scheme Runner \
-configuration Release \
-archivePath Runner.xcarchive \
archive
- name: Export IPA
run: |
cd ios
xcodebuild -exportArchive \
-archivePath Runner.xcarchive \
-exportPath ../build/ios \
-exportOptionsPlist ExportOptions.plist
// Use kDebugMode for debug-only code
if (kDebugMode) {
print('Debug information');
// Debug-only widgets
}
// Use assertions for development checks
assert(() {
// Development-only validation
return true;
}());
Terminal window
# Optimize images before building
find assets -name "*.png" -exec pngquant --force --ext .png {} \;
# Optimize SVG files
find assets -name "*.svg" -exec svgo {} \;
Terminal window
# Analyze APK size breakdown
flutter build apk --analyze-size
# Use bundletool for AAB analysis
bundletool build-apks \
--bundle=build/app/outputs/bundle/release/app-release.aab \
--output=app.apks
bundletool get-size total --apks=app.apks
Terminal window
# Build with size analysis
flutter build ios --analyze-size
# Xcode size report
# Product → Archive → Distribute App → App Store Connect → Upload
# View size report in App Store Connect
# pubspec.yaml - Remove unused dependencies
dependencies:
flutter:
sdk: flutter
# Only include necessary packages
dev_dependencies:
# Only development tools

Android:

android/app/build.gradle.kts
android {
buildTypes {
release {
// Enable R8 full mode
isMinifyEnabled = true
isShrinkResources = true
// Optimize DEX files
multiDexEnabled = false
}
}
}

iOS:

// Optimize Swift compilation
SWIFT_OPTIMIZATION_LEVEL = -O
SWIFT_COMPILATION_MODE = wholemodule
// Enable app thinning
ENABLE_APP_SLICING = YES
.github/workflows/build-and-release.yml
name: Build and Release
on:
push:
tags:
- 'v*'
env:
FLUTTER_VERSION: '3.8.1'
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '11'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Get dependencies
run: flutter pub get
- name: Run tests
run: flutter test
- name: Decode keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/keystore.jks
echo "storeFile=keystore.jks" >> android/key.properties
echo "storePassword=${{ secrets.KEYSTORE_PASSWORD }}" >> android/key.properties
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
- name: Build AAB
run: |
flutter build appbundle \
--release \
--obfuscate \
--split-debug-info=build/debug-info \
--dart-define=PRODUCTION=true \
--dart-define=ENABLE_ANALYTICS=true
- name: Upload AAB artifact
uses: actions/upload-artifact@v3
with:
name: app-release.aab
path: build/app/outputs/bundle/release/app-release.aab
build-ios:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Get dependencies
run: flutter pub get
- name: Install CocoaPods
run: cd ios && pod install
- name: Build iOS
run: |
flutter build ios \
--release \
--obfuscate \
--split-debug-info=build/debug-info \
--dart-define=PRODUCTION=true
scripts/build-android.sh
#!/bin/bash
set -e
echo "Building Android release..."
# Clean previous builds
flutter clean
flutter pub get
# Run tests
flutter test
# Build app bundle
flutter build appbundle \
--release \
--obfuscate \
--split-debug-info=build/debug-info \
--dart-define=PRODUCTION=true \
--dart-define=ENABLE_ANALYTICS=true \
--dart-define=FIREBASE_PROJECT_ID=$FIREBASE_PROJECT_ID
echo "Android build complete!"
echo "Output: build/app/outputs/bundle/release/app-release.aab"
# Optional: Upload to Play Console
if [ "$UPLOAD_TO_PLAY_CONSOLE" = "true" ]; then
echo "Uploading to Play Console..."
# Use Google Play Developer API or fastlane
fi
scripts/build-ios.sh
#!/bin/bash
set -e
echo "Building iOS release..."
# Clean previous builds
flutter clean
flutter pub get
# Install CocoaPods
cd ios && pod install && cd ..
# Run tests
flutter test
# Build iOS
flutter build ios \
--release \
--obfuscate \
--split-debug-info=build/debug-info \
--dart-define=PRODUCTION=true \
--dart-define=ENABLE_ANALYTICS=true
echo "iOS build complete!"
echo "Next: Archive in Xcode and upload to App Store Connect"
Terminal window
# Run all tests before building
flutter test
flutter test integration_test/
# Performance testing
flutter drive --target=test_driver/app.dart
  • App launches successfully
  • All core features work
  • Biometric authentication functions
  • QR code scanning works
  • Cloud backup/restore works
  • App appears correctly in launcher
  • Deep links function properly
  • Performance is acceptable
Terminal window
# Verify obfuscation worked
unzip -l build/app/outputs/bundle/release/app-release.aab | grep classes
# Should show obfuscated class names
Terminal window
# Android: Verify signing
jarsigner -verify -verbose -certs build/app/outputs/bundle/release/app-release.aab
# iOS: Verify provisioning profile
security cms -D -i ios/Runner/embedded.mobileprovision
Terminal window
# Issue: Build fails with "execution failed for task"
# Solution: Clean and rebuild
flutter clean
cd android && ./gradlew clean && cd ..
flutter pub get
flutter build appbundle --release
# Issue: ProGuard errors
# Solution: Update proguard-rules.pro with keep rules
Terminal window
# Issue: Code signing errors
# Solution: Check provisioning profiles and certificates
# Xcode → Preferences → Accounts → Download Manual Profiles
# Issue: Pod install fails
# Solution: Update CocoaPods
cd ios
rm -rf Pods Podfile.lock
pod install --repo-update
cd ..
  1. Android Deployment - Deploy to Google Play
  2. iOS Deployment - Deploy to App Store
  3. Distribution - Alternative distribution methods

Your production builds are now ready for deployment! 🚀