Customize your Flutter app with flavors to target multiple environments like staging and production.Establishing various environments, including Development (dev), Demo (staging), and Production (prod), is a fundamental practice in Flutter app development for mobile applications. Each environment has a distinct role, enabling teams to effectively manage and deliver applications while ensuring high standards of quality.
Benefits of Creating Multiple Flavors
1. Enhanced Collaboration and Workflow
- Independent Development: Developers can operate within their own development environment without disrupting others.
- Isolated Testing: QA teams and stakeholders can perform tests in a demo or flutter app for staging environment, free from interference from ongoing development activities.
- Stable Production: The production environment remains stable and insulated from potential issues arising during development.
2. Tailored Configuration
- Different configurations (e.g., API endpoints, database connections, analytics tools) can be applied to each environment.
- Feature flags can be tested in staging without enabling them in production.
3. Faster Troubleshooting
- Developers can reproduce and debug issues in the flutter app development or staging environment without impacting production.
- Logs and metrics from different environments provide clearer insights into problems.
4. Efficient Deployment Process
- Automated Pipelines: Flutter app builds can be deployed to designated environments through automated pipelines, ensuring consistent and repeatable releases.
- CI/CD Advantages: Continuous Integration and Continuous Deployment (CI/CD) practices thrive with clearly defined environments.
In this tutorial, I’ll guide you step-by-step through the process of adding Multiple Flutter Flavors in the Flutter application using flutter_flavorizr Package. Let’s dive in!
Prerequisites:
Before running Flutter Flavorizr, you must install the following software:
These prerequisites are needed to manipulate Flutter flavor for the iOS and macOS projects and schemes. If you are interested in flavorizing Android only, you can skip this step.
Steps 1 -: Installation
Add the flutter_flavorizr: ^2.2.3
package to the dev_dependencies
section of your project’s pubspec.yaml
file, and then run the flutter pub get
command to install the dependency, as demonstrated below:
Step 2 -: Create flavors
After installing all the necessary prerequisites and adding flutter_flavorizr as a development dependency, you will need to modify your pubspec.yaml
file to define the flavors like below.
flavorizr:
flavors:
dev:
app:
name: "FlavorSample"
android:
applicationId: "com.mobisoft.flavorsampleapp"
ios:
bundleId: "com.mobisoft.flavorsampleapp"
demo:
app:
name: "FlavorSample"
android:
applicationId: "com.mobisoft.flavorsampleapp"
ios:
bundleId: "com.mobisoft.flavorsampleapp"
prod:
app:
name: "FlavorSample"
android:
applicationId: "com.mobisoft.flavorsampleappprod"
ios:
bundleId: "com.mobisoft.flavorsampleappprod"
Code language: CSS (css)
Once the flavor is defined, it will appear as follows.
You can assign a unique bundle ID or package name to each flavor, as I did for the production environment by using a distinct bundle ID.
Step 3-: Executing the Flavorizr Script with flutter pub run
Having defined the Flavorizr configuration, we can now move forward by running the script with the command flutter pub run flutter_flavorizr
Upon executing the command, you will notice that the following files have been generated in the lib folder: flavors.dart, app.dart, main_dev.dart, main_demo.dart, and main_prod.dart. Additionally, a pages folder will be created, containing the my_home_page.dart
file.
The script will remove the default generated code from the main.dart
file, which may result in an error in the widget_test.dart
file located in the test folder. To fix this, simply replace the error code with await tester.pumpWidget(const App());
Typically, we prefer to create our own screens. Therefore, I am deleting the script-generated my_home_page.dart
and app.dart
files. However, if you wish, you can choose to use them and continue with this setup
Step 4 -: Organizing and Configuring Flavor Files for Setup and Initialization
To keep all flavor-related files organized in one location, create a folder named flavors and then create a file called build_flavor.dart
inside it. Add the following code to this file. This section enables you to perform additional setups, such as configuring Firebase or initializing the database, before calling runApp(MyApp()).
Additionally, move the flavors.dart
file into the flavors folder to maintain organization.
void buildFlavor(Flavor flavor) {
F.appFlavor = flavor;
runApp(const App());
}
Code language: JavaScript (javascript)
Invoke the buildFlavor()
function in the main_dev, main_demo, and main_prod files, utilizing the correct flavor parameter. The code in your main files should look like this
main_dev.dart -:
Future<void> main() async {
buildFlavor(Flavor.dev);
}
Code language: JavaScript (javascript)
main_demo.dart -:
Future<void> main() async {
buildFlavor(Flavor.demo);
}
Code language: JavaScript (javascript)
main_prod.dart -:
Future<void> main() async {
buildFlavor(Flavor.prod);
}
Code language: JavaScript (javascript)
Step 5-: Setting Up Your Flavors
Now, let’s move on to the next step, where we configure the setup using the main files generated by flutter_flavorizr.
1) Open the Run/debug configuration dialog and choose the Edit Configuration option
2) Click the Add New Configuration button (or use ⌘ + N) located at the top-right corner of the window, and select Flutter. Refer to the image below for guidance.
3) Dev Configuration: Rename the flutter build configuration to dev and specify the path to the main_dev.dart
file in the Dart Entrypoint field. Include –flavor dev in the Additional Run Args field. After filling in all the necessary fields, click the OK button.
Note: The argument name should match your flavor name.
Fields and their descriptions:
- Name: Specifies the name of the flavor.
- Dart entrypoint: Defines the path to the respective main file for the flavor.
- Additional run args: Allows you to add extra command-line arguments to be passed to the Flutter application during execution.
- Store as project file: When checked, saves the configuration directly in the project files, making it easier to share and manage run settings across teams or different environments
If you’re having trouble adding the path of your main file, you can click the browse button at the end of the field to choose the correct main file. Please see the image below for reference
4) Demo Configuration: Add a new configuration, rename it to demo, and specify the path to the main_demo.dart
file in the Dart Entrypoint field. Include --flavor demo
in the Additional Run Args field.
5) Prod Configuration: Add another new configuration, rename it to prod, and specify the path to the main_prod.dart
file in the Dart Entrypoint field. Additionally, include --flavor prod
in the Additional Run Args field.
After completing all configurations, you can remove the main target flavor. Once the setup is finished, your configurations should look like this
Step 6 -: Creating a Flavor-Based Text Display Screen
We have completed all the setup. Now, let’s proceed to the example where we create a screen that displays text with colors corresponding to each flavor.
- Create a folder for the screens and add a file named
welcome_screen.dart
inside it. In this file, we will create a text label and assign its title property, which is automatically generated inflavors.dart
, to that label. I have updated the title property with a different text for each flavor, as shown in the following code fromflavors.dart
.
class F {
static Flavor? appFlavor;
static String get name => appFlavor?.name ?? '';
static String get title {
switch (appFlavor) {
case Flavor.dev:
return 'Development';
case Flavor.demo:
return 'Demo';
case Flavor.prod:
return 'Prod';
default:
return 'title';
}
}
}
Code language: JavaScript (javascript)
Let’s create a text property in the welcome_screen.dart
file and set the title text and color according to the flavor. You can refer to the following code for details
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
'Welcome to ${F.title} Flavor',
style: TextStyle(fontSize: 21, color: _getTextColor()),
textAlign: TextAlign.center,
),
),
),
),
);
}
Color _getTextColor() {
switch (F.appFlavor) {
case Flavor.dev:
return Colors.red;
case Flavor.demo:
return Colors.blue;
case Flavor.prod:
return Colors.green;
default:
return Colors.white;
}
}
}
Call the initial welcome screen within the buildFlavor function as shown below.
void buildFlavor(Flavor flavor) {
F.appFlavor = flavor;
runApp(const WelcomeScreen());
}
Code language: JavaScript (javascript)
This is how the welcome screen appears for various flavors after execution.
Summary
I hope you found this tutorial on Customizing your Flutter app with flavors for different environments helpful. To download the source code for the sample app, click here.
Author's Bio
Abhijit Muthe is a Principal Software Engineer at Mobisoft Infotech, bringing over 8.5 years of experience in mobile application development. He specializes in iOS, Flutter, and Xamarin technologies, demonstrating a strong commitment to creating innovative and user-friendly applications