In Flutter development, managing different configurations for various environments such as development, staging, and production is a common requirement. Build flavors and build configurations come to the rescue, allowing you to tailor your app’s behavior, assets, and settings based on the environment it’s being built for. This article dives deep into how to effectively utilize build flavors and environments in Flutter.
What are Build Flavors in Flutter?
Build flavors are different versions of your app built from the same codebase. They are particularly useful when you want to have multiple variants of your app, such as a free version with limited features and a paid version with full access. In Flutter, build flavors are set up using Flutter’s build configuration system.
Why Use Build Flavors?
- Environment-Specific Settings: Tailor configurations (API endpoints, app names, bundle IDs) for different environments (dev, staging, prod).
- Feature Toggling: Enable or disable specific features based on the build flavor.
- Multiple Branding: Support multiple brands or themes within a single codebase.
- Simplified Testing: Streamline testing on different environments by pre-configuring settings.
How to Implement Build Flavors in Flutter
Step 1: Configure Flavors in flutter_config.yaml
Create or modify the flutter_config.yaml
file in the root of your Flutter project to define your flavors. This file allows you to manage different build configurations for different environments.
flavors:
development:
app_name: "My App Dev"
api_url: "https://dev.example.com/api"
staging:
app_name: "My App Staging"
api_url: "https://staging.example.com/api"
production:
app_name: "My App"
api_url: "https://example.com/api"
In this example, we define three flavors: development
, staging
, and production
. Each flavor has an app_name
and an api_url
.
Step 2: Install the flutter_config
Package
Add the flutter_config
package to your pubspec.yaml
file.
dependencies:
flutter_config: ^2.0.0
Then run flutter pub get
to install the package.
Step 3: Access Flavor-Specific Values in Code
Use the FlutterConfig
class to access your flavor-specific values.
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 4: Run Your App with a Specific Flavor
To run your app with a specific flavor, use the --flavor
option in the flutter run
command.
flutter run --flavor development
Step 5: Define Build Configurations in Gradle (Android) or Xcode (iOS)
For Android, modify your build.gradle
file to include different build configurations for each flavor. Here’s an example:
android {
flavorDimensions "default"
productFlavors {
development {
dimension "default"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
resValue "string", "app_name", "My App Dev"
}
staging {
dimension "default"
applicationIdSuffix ".staging"
versionNameSuffix "-staging"
resValue "string", "app_name", "My App Staging"
}
production {
dimension "default"
resValue "string", "app_name", "My App"
}
}
}
For iOS, you can configure flavors using Xcode schemes. Duplicate your existing scheme and modify it for each flavor.
Managing Environments in Flutter
Besides flavors, managing environments involves using environment variables to configure different aspects of your app at runtime.
Step 1: Using Environment Variables
Add a .env
file for each environment in your project root.
# .env.development
APP_NAME=My App Dev
API_URL=https://dev.example.com/api
# .env.staging
APP_NAME=My App Staging
API_URL=https://staging.example.com/api
# .env.production
APP_NAME=My App
API_URL=https://example.com/api
Step 2: Loading Environment Variables
Use the flutter_dotenv
package to load environment variables into your app. Add the package to your pubspec.yaml
:
dependencies:
flutter_dotenv: ^5.0.2
Then, load the environment variables in your main.dart
file:
import 'package:flutter_dotenv/flutter_dotenv.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: ".env.development"); // Load .env file
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: dotenv.env['APP_NAME'] ?? 'My App',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(dotenv.env['APP_NAME'] ?? 'My App'),
),
body: Center(
child: Text('API URL: ${dotenv.env['API_URL'] ?? 'N/A'}'),
),
);
}
}
Step 3: Setting up different entry points
Create different entry point dart files, such as main_dev.dart, main_stage.dart and main_prod.dart files, and have flavor dependent env files loading
//main_dev.dart
import 'package:flutter_dotenv/flutter_dotenv.dart';
Future main() async {
await dotenv.load(fileName: ".env.development"); // Load .env file
runApp(MyApp());
}
Step 4: Build command with entry points and flavors
Run command for a specific entrypoint
flutter run -t main_dev.dart --flavor development
Advanced Tips and Best Practices
- CI/CD Integration: Automate builds for different flavors using CI/CD tools like Jenkins, GitLab CI, or GitHub Actions.
- Secure Secrets: Store sensitive information (API keys, passwords) securely using tools like HashiCorp Vault or cloud provider secrets managers.
- Dynamic Configuration: Use remote configuration services (Firebase Remote Config) for dynamic settings updates without requiring app updates.
- Version Control: Manage your configuration files carefully with version control, and ensure sensitive data is never committed to the repository.
Conclusion
Utilizing build flavors and environments in Flutter is essential for managing different configurations across development, staging, and production. By setting up flavors with Flutter’s build configuration system and leveraging environment variables with packages like flutter_dotenv
, you can efficiently tailor your app’s behavior and settings for each environment. Properly managing flavors and environments leads to more maintainable, scalable, and robust Flutter applications.