In Flutter development, managing different versions or configurations of your app can become complex, especially when dealing with varying APIs, branding, or features for different environments (like development, staging, and production). Build flavors are a powerful way to handle these scenarios. This blog post will guide you through utilizing build flavors in Flutter to manage different configurations of your app efficiently.
What are Build Flavors?
Build flavors allow you to create different versions of your Flutter app from the same codebase. Each flavor can have its own set of configurations, such as different app icons, names, API endpoints, and features. This is especially useful when you need to manage multiple environments (e.g., development, staging, production) or offer different versions of your app with distinct branding.
Why Use Build Flavors?
- Configuration Management: Easily manage different configurations for different environments.
- Code Reusability: Maintain a single codebase while creating multiple versions.
- Branding: Customize app branding (e.g., app icons, names) for each version.
- Feature Toggles: Enable or disable specific features based on the build flavor.
How to Implement Build Flavors in Flutter
Here’s a step-by-step guide on implementing build flavors in your Flutter project:
Step 1: Configure Build Flavors in android/app/build.gradle
Open the android/app/build.gradle file and add the flavorDimensions and productFlavors blocks inside the android block.
android {
...
flavorDimensions "environment"
productFlavors {
dev {
dimension "environment"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
resValue "string", "app_name", "My App Dev"
buildConfigField "String", "API_URL", ""https://dev.example.com/api/""
}
staging {
dimension "environment"
applicationIdSuffix ".staging"
versionNameSuffix "-staging"
resValue "string", "app_name", "My App Staging"
buildConfigField "String", "API_URL", ""https://staging.example.com/api/""
}
prod {
dimension "environment"
applicationIdSuffix ".prod"
resValue "string", "app_name", "My App"
buildConfigField "String", "API_URL", ""https://example.com/api/""
}
}
...
}
In this example:
flavorDimensions "environment"defines a flavor dimension named “environment.”dev,staging, andprodare the product flavors representing the development, staging, and production environments.applicationIdSuffixappends a suffix to the application ID.versionNameSuffixadds a suffix to the version name.resValuesets a string resource value (app name).buildConfigFielddefines a build configuration field (API URL).
Step 2: Access Build Configuration Fields in Dart Code
To access the build configuration fields in your Dart code, you need to add the flutter_config dependency to your pubspec.yaml file.
dependencies:
flutter:
sdk: flutter
flutter_config: ^2.0.0
Then, initialize FlutterConfig in your main.dart file.
import 'package:flutter/material.dart';
import 'package:flutter_config/flutter_config.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterConfig.loadEnvVariables();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: FlutterConfig.get('APP_NAME'),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(FlutterConfig.get('APP_NAME')),
),
body: Center(
child: Text('API URL: ${FlutterConfig.get('API_URL')}'),
),
);
}
}
Step 3: Configure iOS Flavors
For iOS, you need to configure schemes and build configurations in Xcode. Open ios/Runner.xcworkspace with Xcode.
Step 3.1: Add Build Configurations
- Go to Project > Runner > Info > Configurations.
- Duplicate the
DebugandReleaseconfigurations for each flavor (e.g.,Debug-dev,Release-dev,Debug-staging,Release-staging, etc.).
Step 3.2: Add Schemes
- Go to Product > Scheme > New Scheme.
- Name the scheme after your flavor (e.g.,
dev,staging). - Edit the scheme and configure the Build, Run, Test, and Profile actions to use the appropriate build configurations (e.g.,
Debug-dev,Release-dev).
Step 3.3: Define Preprocessor Definitions
In the Build Settings for each configuration, define preprocessor definitions:
DEV=1
STAGING=1
PROD=1
Step 3.4: Add User-Defined Settings
Also in Build Settings, add user-defined settings:
APP_NAME = My App Dev
API_URL = https://dev.example.com/api/
Step 4: Access Flavors in iOS Dart Code
To access environment variables in Dart code for iOS, use the dart-define flag. This requires configuring build scripts to inject these variables at compile time.
Install CocoaPods dependencies with flutter pub get then configure Build Phases.
- Go to Target > Runner > Build Phases.
- Click + > New Run Script Phase
Run script with configuration to insert the API_URL variable. This solution does not need a plugin dependency because API_URL variable is included when the Flutter build occurs.
echo "API_URL: $API_URL"
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $APP_NAME" "${INFOPLIST_FILE}"
echo "Running flutter build..."
flutter build ios --release
--build-name=1.0.0
--build-number=1
--dart-define=API_URL=$API_URL
Now get env variables via the code:
String apiUrl = const String.fromEnvironment('API_URL', defaultValue: 'https://example.com');
Text('API URL: $apiUrl'),
Step 5: Run Your App with Different Flavors
To run your Flutter app with a specific flavor, use the following commands:
For Android:
flutter run --flavor dev --target lib/main_dev.dart
flutter run --flavor staging --target lib/main_staging.dart
flutter run --flavor prod --target lib/main_prod.dart
For iOS:
flutter run --flavor dev --target lib/main_dev.dart --scheme dev
flutter run --flavor staging --target lib/main_staging.dart --scheme staging
flutter run --flavor prod --target lib/main_prod.dart --scheme prod
Advanced Usage and Tips
- Separate Main Files: Use separate
main.dartfiles (e.g.,main_dev.dart,main_staging.dart) to initialize flavor-specific configurations. - Conditional Logic: Use conditional logic in your Dart code based on the build flavor.
- Environment Variables: Utilize environment variables for sensitive information like API keys.
Conclusion
Build flavors are an essential tool for managing different configurations of your Flutter app efficiently. By configuring build flavors for Android and iOS, you can maintain a single codebase while creating multiple versions with distinct configurations, branding, and features. This approach simplifies development, testing, and deployment, making your Flutter projects more manageable and scalable.