Mobile Application

A Comprehensive Guide to Implementing Flutter Flavors

Introduction

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.

What are Flutter Flavors? 

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.

Advantages of Using Flavors in Development

1. Different API Endpoints for Development and Production

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:

  • Development Environment: This might use a test server where developers can experiment with new features, debug issues, and perform extensive testing without affecting real users. The API endpoint for this environment is typically something like dev.api.demoexamlpe.com.
  • Production Environment: This refers to the live environment where real users engage with the application. It needs to be stable, secure, and performant. The API endpoint here would be something like api.demoexamlpe.com.

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.

2. Unique App Icons, Themes, and Names for Each Environment

 Flavors allow an application to have distinct visual identities for different environments. This can include:

  • App Icons: Different icons for development and production versions help developers and testers quickly identify which version of the app they are using. For example, a development app might have a small “DEV” badge on the icon.
  • Themes: Different color schemes or themes can be applied to distinguish environments visually. A development app might use a blue theme, while the production app uses a green theme.
  • Names:  Implementing distinct app names for different build variants, like “MyApp (Development)” and “MyApp”, can streamline the development process.
See also  Custom Activity Life Cycle Components for Android

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.

3. Custom Configurations

 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.

  • Debugging Tools: Development flavors can include additional debugging tools and logs that help developers identify and fix issues more efficiently. These tools and logs can be excluded from the production flavor to optimize performance and security.
  • API Keys and Secrets:  Independent API keys or secrets for each environment serve as a security cornerstone. By preventing resource crossover, such as employing distinct Google Maps API keys for development and production, potential issues and unnecessary costs are mitigated.

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:

  • Holds static variables for baseurl, environmentName, and imagePath.
  • Contains a static method setUpEnv to set the configuration based on the environment passed.

setUpEnv Method:

  • Accepts an Environment parameter.
  • Sets the static variables (baseurl, environmentName, imagePath) according to the environment:
    • dev: http://api.qa.aaaaaaaaaa.com, DEV, assets/images/dev/mang_image.png
    • uat: http://api.qa.bbbbbbb.com, UAT, assets/images/uat/employee_image.png
    • prod: https://api.cccccccccc.net, PROD, assets/images/prod/bo_image.png
  • Default case resets the values to empty strings if the environment doesn’t match any predefined environment.
See also  Integrate Postgres Database Connection in Flutter

Common Entry Point

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(),

     },

   );

 }

}

Main Files for Each Flavor

We then create separate main files for each environment  (DEV, UAT and PROD) that call the commonmain function after setting up the environment.

Main Files for DEV

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();

}

Main Files for UAT

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();

}

Main Files for PROD

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.

  • dimension “default”Associates each flavor with the “default” dimension.
  • resValue “string”, “app_name”, …   Sets a different app name for each flavor.
  • applicationIdSuffix “.dev”:   Adds a suffix to the application ID for each flavor to differentiate them.
See also  What Are The Advantages of Kotlin Over Java?

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.

Commands to build and run our flavor

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*

FAQ’s

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.

lets start your project

Related Articles