In this post, we will go over how we can configure development, staging and production environment configuration in Flutter app. We will create different environment configurations and load the right environment during runtime.
Introduction To Environment Configuration
In any app development process, we need to create different environments to run the app in.
For example, if you are developing against your local environment and connecting with API server on local machine, then your api url needs to point to localhost.
Similarly, if you are testing the app in a staging environment which is a replica of production environment, then your app needs to use urls for the staging endpoints. Finally, when you want to release the app, it should be using the production endpoints.

Not only the API endpoints, any app also needs to consider these environment variables for other purposes as well.
You might want to track various events for analytics purposes in the app; but only in the production environment. You might need to display mobile ads in the production environment. Or you might want to send error reports in the Staging and Production environment only.
So, how can we configure development, staging and production environments in Flutter app?
Define App Environments
We start by defining an abstract class BaseConfig
which will hold our app environment variables.
Define BaseConfig Environment
abstract class BaseConfig {
String get apiHost;
bool get useHttps;
bool get trackEvents;
bool get reportErrors;
}
The BaseConfig
class has various fields:
apiHost
represents API Endpoint host serveruseHttps
checks to use HTTPS when making API callstrackEvents
checks to track user activities like Firebase events in appreportErrors
represents whether to report runtime errors in app or not
We can have environment variables and fields as needed in the app here.
Next, we need implement the BaseConfig
class for each environment.
Configuration For Development Environment
The development environment configuration could look like this:
class DevConfig implements BaseConfig {
String get apiHost => "localhost";
bool get reportErrors => false;
bool get trackEvents => false;
bool get useHttps => false;
}
Configuration For Staging Environment
Similarly, the staging environment configuration could look like this:
class StagingConfig implements BaseConfig {
String get apiHost => "staging.example.com";
bool get reportErrors => true;
bool get trackEvents => false;
bool get useHttps => true;
}
Configuration For Production Environment
Finally, the production environment configuration:
class ProdConfig implements BaseConfig {
String get apiHost => "example.com";
bool get reportErrors => true;
bool get trackEvents => true;
bool get useHttps => true;
}
We have now defined classes for development, staging and production environments in Flutter app. Next, we need to make sure our app uses these environments properly.
Load Environment Configuration In Flutter App
In order to load proper environment configuration in our Flutter app, we will create a class called Environment
which can set configuration dynamically.
class Environment {
factory Environment() {
return _singleton;
}
Environment._internal();
static final Environment _singleton = Environment._internal();
static const String DEV = 'DEV';
static const String STAGING = 'STAGING';
static const String PROD = 'PROD';
BaseConfig config;
initConfig(String environment) {
config = _getConfig(environment);
}
BaseConfig _getConfig(String environment) {
switch (environment) {
case Environment.PROD:
return ProdConfig();
case Environment.STAGING:
return StagingConfig();
default:
return DevConfig();
}
}
}
Since the Environment
class has a singleton implementation, this ensures that the environment configuration doesn’t change through out the application lifecycle.
Initialize Environment Configuration
Now, all that’s left to do is initialize the environment configuration in our app. We should do this at the main entry point for the app i.e. inside the main.dart
main
() function.
void main() {
const String environment = String.fromEnvironment(
'ENVIRONMENT',
defaultValue: Environment.DEV,
);
Environment().initConfig(environment);
runApp(MyApp());
}
We have modified the main
method to initialize the app environment during runtime. The main method expects a argument named ENVIRONMENT
whose default value is DEV
.
So, by default the app runs in development environment if no other value is specified.
Get Environment Variable Value When Needed In App
Once the environment is initialized, we can make use of the config values anywhere inside the app by doing something like this:
// somewhere inside your app where you are make HTTP call
final String apiHost = Environment().config.apiHost;
final bool useHttps = Environment().config.useHttps;
If you have been paying attention until now, I am sure you are wondering, “Everything’s looking great, but how do I pass the argument to the main method?“
Well I will leave that to you to figure out for now 😛
Just kidding!
Starting with Flutter 1.17, Flutter supports receiving additional arguments to the main function using the --dart-define
keywords.
So, we can use this to tie things up for configuring development, staging and production environments in Flutter.
Run the app in staging mode:
flutter run --dart-define=ENVIRONMENT=STAGING
Run the app in production mode:
flutter run --dart-define=ENVIRONMENT=STAGING
Or run it in development mode:
flutter run --dart-define=ENVIRONMENT=STAGING
//or simply
flutter run
You can setup these arguments in your Makefile
or also in your CI/CD pipelines as well.
You must be logged in to post a comment.