Leveraging Flutter’s single code base for many platforms, developers can form a unified, cohesive, and good user experience for users on many platforms. But maintaining a diverse environment and configuration in your code base might be a cumbersome and tricky problem. Say hello to Flutter Flavors and wield a powerful weapon in our arsenal.
Flutter flavors are like build variants within Android or targets within iOS. Flavors allow us to configure an application in different ways for different environments, subject to no code duplication. This becomes even most useful when you’re doing testing and deployment.
Utilizing distinct API endpoints for development and production stages is essential in software development, particularly for mobile applications. Flavors allow developers to define separate configurations for these environments. For instance:
Using flavors ensures that these environments are kept separate, preventing accidental deployment of unfinished features to production and ensuring that development activities do not interfere with the live application.
Flavors allow an application to have distinct visual identities for different environments. This can include:
This visual separation acts as a preventative measure against environment confusion, ensuring that developers, testers, and other project personnel are always aware of the specific app context.
By utilizing build flavors, developers can create distinct app configurations for various environments. Feature flags allow selectively enabling or disabling functionalities within specific build variants.
Feature Flags: Specific features can be enabled or disabled based on the environment. For instance, experimental features might be enabled in the development flavor but hidden in the production flavor.
Custom configurations provided by flavors optimize the development workflow by enhancing flexibility, security, and efficiency. This ensures each environment aligns precisely with its intended purpose.
Configuration Files in Flutter
In this file, we have an enum Environment for the different environments (dev, uat, prod) and an abstract class AppEnvironment that holds the configuration values for each environment. The setUpEnv method sets the appropriate values based on the environment passed to it.
enum Environment { dev, uat, prod }
abstract class AppEnvironment {
static late String baseurl;
static late String environmentName;
static late String imagePath;
static late Environment _environment;
static Environment get environment => _environment;
static setUpEnv(Environment environment) {
_environment = environment;
switch (environment) {
case Environment.dev:
baseurl = 'http://api.qa.aaaaaaaaaa.com';
environmentName = 'DEV';
imagePath = 'assets/images/dev/mang_image.png';
break;
case Environment.uat:
baseurl = 'http://api.qa.bbbbbbb.com';
environmentName = 'UAT';
imagePath = 'assets/images/uat/employee_image.png';
break;
case Environment.prod:
baseurl = 'https://api.cccccccccc.net';
environmentName = 'PROD';
imagePath = 'assets/images/prod/bo_image.png';
break;
default:
baseurl = '';
environmentName = '';
imagePath = '';
break;
}
}
}
AppEnvironment Class:
setUpEnv Method:
Next, we create a common entry point for the app that will be used across different environments.This commonmain function initializes the app with a MyApp widget. The MyApp widget configures the MaterialApp appropriately according to the specific environment
import ‘package:blog/welcome_page.dart’;
import ‘package:http/http.dart’ as http;
import ‘package:blog/flavor_config.dart’;
import ‘package:flutter/material.dart’;
void commonmain() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘Flutter Flavor ${AppEnvironment.environmentName}’,
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
routes: {
‘/home’: (context) => const MyHomePage(),
‘/welcomePage’: (context) => const WelcomePage(),
},
);
}
}
We then create separate main files for each environment (DEV, UAT and PROD) that call the commonmain function after setting up the environment.
This file imports the necessary modules and sets up the environment to dev using the AppEnvironment.setUpEnv method. After the environment is configured, it calls commonmain to run the app with development settings.
import ‘package:blog/common_main.dart’;
import ‘package:blog/flavor_config.dart’;
import ‘package:flutter/material.dart’;
void main() {
WidgetsFlutterBinding.ensureInitialized();
AppEnvironment.setUpEnv(Environment.dev);
commonmain();
}
This file sets up the environment to uat for the User Acceptance Testing configuration. Like the other main files, it initializes the necessary bindings and then calls commonmain to run the app with UAT settings.
import ‘package:blog/common_main.dart’;
import ‘package:blog/flavor_config.dart’;
import ‘package:flutter/material.dart’;
void main() {
WidgetsFlutterBinding.ensureInitialized();
AppEnvironment.setUpEnv(Environment.uat);
commonmain();
}
This file establishes the production environment by setting configuration values suitable for deployment. It ensures all necessary bindings are initialized and then calls commonmain to run the app with production settings.
import ‘package:blog/common_main.dart’;
import ‘package:blog/flavor_config.dart’;
import ‘package:flutter/material.dart’;
void main() {
WidgetsFlutterBinding.ensureInitialized();
AppEnvironment.setUpEnv(Environment.prod);
commonmain();
}
Configuring Flavors for Android and iOS in build.gradle file
Configure flavors for Android and iOS in our Flutter project to support multiple environments, customizing settings like application ID and resource values for each build.
flavorDimensions “default”
productFlavors {
dev {
dimension “default”
resValue “string”, “app_name”, “OC Management”
applicationIdSuffix “.dev”
}
uat {
dimension “default”
resValue “string”, “app_name”, “OC Developer”
applicationIdSuffix “.uat”
}
prod {
dimension “default”
resValue “string”, “app_name”, “OC Boss”
applicationIdSuffix “.prod”
}
}
flavorDimensions “default”: Defines a dimension for the flavors. A dimension is a category for flavors, which allows us to create multiple flavor dimensions if needed.
productFlavors: Defines different flavors (dev, uat, prod) and their specific configurations.
What’s new we used …..?
Streamlining asset management across app flavors is now easier with Flutter 3.22’s new feature.we can specify which assets belong to which flavors in the pubspec.yaml file. Here’s how we can configure it:
assets:
– assets/common/
– path: assets/images/dev/
flavors:
– dev
– path: assets/images/uat/
flavors:
– uat
– path: assets/images/prod/
flavors:
– prod
Advantage of This Approach
Simplify Asset Management: Clearly organize and manage assets for different environments within a single configuration file.
Reduce Build Complexity: Automatically include only the relevant assets for each build, minimizing the risk of errors and reducing build times.
Improve Code Maintenance: Keep environment-specific assets separated, making it easier to update and maintain them without affecting other environments.
For Development : flutter run -t lib/main_dev.dart
For Staging : flutter run -t lib/main_staging.dart
For Production : flutter run -t lib/main_prod.dart
For launching app icon : flutter pub run flutter_launcher_icons:main -f flutter_launcher_icons*
Que – Working with android studio could be easy to run the program
But while using vs code it dosen’t find the route
Ans – Working with android studio could be easy to run the program
But while using vs code it dosen’t find the route for this we have to
Set our launch.json
Example
{
“version”: “0.2.0”,
“configurations”: [
{
“name”: “blog (dev)”,
“request”: “launch”,
“type”: “dart”,
“program”: “lib/main_dev.dart”,
“args”: [“–flavor”, “dev”]
},
{
“name”: “blog (uat)”,
“request”: “launch”,
“type”: “dart”,
“program”: “lib/main_uat.dart”,
“args”: [“–flavor”, “uat”]
},
{
“name”: “blog (prod)”,
“request”: “launch”,
“type”: “dart”,
“program”: “lib/main_prod.dart”,
“args”: [“–flavor”, “prod”]
},
}
Que: What should I do if I receive a deprecated command warning when setting launcher icons?
Ans: If you receive a deprecated command warning, use dart run instead of flutter pub run. For example, use:
dart run flutter_launcher_icons:main -f flutter_launcher_icons.yaml
Que : What are the benefits of managing assets per flavor in Flutter 3.22?
Ans : compared to the older method of including all assets in every build, managing assets per flavor helps in optimizing build sizes by including only necessary assets, thereby reducing unnecessary bloat.
Diving deep into SwiftUI This blog post drops us into…
Corporate efficiency and customization are vital in today's fast-paced world,…
Flutter Codemagic CI/CD makes your Flutter app build, test, and…
This website uses cookies.