I am building a Flutter app, and I have variables with different values for different environments (QA, dev, prod, etc). What's a good way to organize my app so I can easily make a build for QA, dev, prod, and other environments?
9 Answers
Building on Seth's idea, here's an example that sets up a global representing the BuildEnvironment
named env
.
env.dart
import 'package:meta/meta.dart';
enum BuildFlavor { production, development, staging }
BuildEnvironment get env => _env;
BuildEnvironment _env;
class BuildEnvironment {
/// The backend server.
final String baseUrl;
final BuildFlavor flavor;
BuildEnvironment._init({this.flavor, this.baseUrl});
/// Sets up the top-level [env] getter on the first call only.
static void init({@required flavor, @required baseUrl}) =>
_env ??= BuildEnvironment._init(flavor: flavor, baseUrl: baseUrl);
}
main_dev.dart
import 'package:flutter/material.dart';
import 'env.dart';
import 'app.dart';
void main() {
BuildEnvironment.init(
flavor: BuildFlavor.development, baseUrl: 'http://dev.example.com');
assert(env != null);
runApp(App());
}
main_prod.dart
import 'package:flutter/material.dart';
import 'env.dart';
import 'app.dart';
void main() {
BuildEnvironment.init(
flavor: BuildFlavor.production, baseUrl: 'http://example.com');
assert(env != null);
runApp(App());
}
- import
env.dart
to expose theenv
variable. run and build the app using the
target
option.flutter run -t lib/main_dev.dart flutter build -t lib/main_dev.dart
To integrate with VS Code, define launch configurations:
.vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "development",
"program": "lib/main_dev.dart",
"request": "launch",
"type": "dart"
},
{
"name": "production",
"program": "lib/main_prod.dart",
"request": "launch",
"type": "dart"
}
]
}
I had originally set out to use command line arguments passed to Dart's main
function, but I don't think args can currently be passed on the command line with flutter run
or flutter build
, although VS Code and Android Studio both support passing args to main
. It also seems build flavor as a command line arg to main
is not appropriate since args can be passed after the build process.

- 8,841
- 3
- 51
- 66
-
this should be marked as the right useful answer as one can change the additional run configs in the IDE to point to using this solution – Fred Grott Jul 07 '19 at 11:23
-
7How would you use this solution and keep your secret API keys and config settings out of source control? – MaylorTaylor Jul 15 '19 at 22:24
-
Is there anyway you could do this solution and still have `flutter run` launch into Development mode by default? It's a bit annoying to add all the extra commands (`flutter run -t lib/main_dev.dart`) – MaylorTaylor Jul 15 '19 at 23:39
-
you're IDE may be able to handle that for you. The answer includes how to do it in VS Code. – Jacob Phillips Jul 17 '19 at 18:07
-
Hello, is there a solution by using different Google Services (like Firebase) which requires different config for dev and release? It seems we cannot Google services change config file name. – camillo777 Mar 24 '20 at 09:08
-
1@camillo777 [This article](https://android.jlelse.eu/how-to-use-different-google-services-json-file-with-multiple-product-flavors-android-7853d98dd6c0) explains it for Android, nothing Flutter specific. – Jacob Phillips Mar 25 '20 at 18:41
Release and debug mode can now be acquired using
const bool isProduction = bool.fromEnvironment('dart.vm.product');
Because this is a constant it works with tree-shaking.
So code like
if(isProduction) {
// branch 1
} else {
// branch 2
}
would only include one of these two branches into production code depending on isProduction

- 623,577
- 216
- 2,003
- 1,567
-
This seems like a good solution, is there a reason it did not get more traction as the answer? – SamIAmHarris Jan 30 '19 at 01:51
-
Perhaps because it's an older question and I just posted the answer 3 months ago. – Günter Zöchbauer Jan 30 '19 at 03:53
-
4If used all over the project, it may be prone to errors, in my opinion. Better use this only when initialising the application. – droid8421 Jun 12 '19 at 12:43
-
@droid8421 did you really downvote my answer because you think the code I suggestion should not be everywhere in your app?? – Günter Zöchbauer Jun 12 '19 at 12:59
-
1This value is good, but still couldn't fulfill the question, since it's said `dev/qa/prod`. This value only identify `dev/qa` or `prod`. – JerryZhou Sep 01 '19 at 09:41
One way to do it: create different main_<environment>.dart
files in the lib/
directory of your project.
Each main_<environment>.dart
contains the environment-specific configurations/values (such as the different database names, etc). Each main_<environment>.dart
then imports the actual application library and runs the application, passing in the environment's values/configurations.
Then, choose which .dart
file to build: flutter run -t lib/main_debug.dart

- 112,095
- 66
- 196
- 279
-
1I believe that will only work when developing, the native sides that use `FlutterView` still use `runFromBundle` with "main". https://github.com/flutter/engine/blob/master/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java#L177 – Simon Mar 02 '18 at 15:08
-
@Simon maybe that problem is fixed by the build process e.g. `flutter build apk -t lib/main_debug.dart`? – Jacob Phillips Jun 23 '18 at 23:09
-
@Jacob I committed a fix for it not working for iOS. I believe it's in the dev branch. – Simon Jun 24 '18 at 03:48
-
@Simon As you told, native sides (Android Studio in my case) not takes neither main_dev.dart nor main_prod.dart, it still running the "main" file, so How can I tell Android Studio which file run on Flutter side when pressing the "play" button?, Thanks in Advance – SaloGala Jun 26 '18 at 01:10
Starting from Flutter 1.17 you can use --dart-define
to build your app with different compile time variables. It works for both Dart and native layers. In dart you get these values with String.fromEnvironment
for example. In that way you won't need to have tons or entry points and expose your environment credentials
Here is an article that explains more https://link.medium.com/ibuTsWHrk6

- 2,019
- 1
- 9
- 15
-
This solution is great. The article shows how to create multiple builds for each environment and also makes the configuration MUCH easier on the iOS configuration side to produce multiple apps with the same code. – Nick N Jan 16 '22 at 20:48
-
1Okay that's something useful. But you missed the point of explaining how to handle "different values for different environments (QA, dev, prod, etc)". – Iván Yoed Feb 13 '22 at 04:13
-
1@IvánYoed you can use dart defines to pass different values depends on the target environment. For example if you have bitrise or codemagic account, usually you going to have multiple workflows for multiple environments. There you can use env variables to pass needed – tatsuDn Feb 14 '22 at 07:45
import 'package:flutter/foundation.dart';
And use following const values:
if (kReleaseMode) {
// App is running in release mode.
} else if (kProfileMode) {
// App is running in profile mode.
} else if (kDebugMode) {
// App is running in debug mode.
} else if (kIsWeb) {
// App is running on the web.
}

- 237,138
- 77
- 654
- 440
-
1I strongly believe that one is the most simple and elegant solution for most cases. – EmreSURK Jun 03 '21 at 19:43
The cleaner way to do this is through Build Flavors.
As a quick example, if you want to have a different app id for your app for your "dev" build, you could have this in the gradle file:
flavorDimensions "version"
productFlavors {
dev {
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
}
}
Read more about gradle build variant configuration here.
Now you can run with this build variant with command line:
flutter run --flavor dev
If you are using Android Studio, you can set the build variant in the Run configuration too:
Read more about iOS configuration on this blog. And official flutter documentation about build flavours.

- 30,852
- 26
- 164
- 234
Simply you can implement build variants.
In android:
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
}
debug{
applicationIdSuffix ".dev"
signingConfig signingConfigs.debug
}
qa{
applicationIdSuffix ".qa"
signingConfig signingConfigs.qa
}
}
productFlavors {
dev {
dimension "app"
resValue "string", "app_name", "xyz Dev"
applicationId "com.xyz.dev"
}
prod {
dimension "app"
resValue "string", "app_name", "xyz"
}
}
In iOS :
add configuration by selecting project->runner-> configuration add one more

- 2,745
- 6
- 19
- 29
-
3
-
do we have something similar that appends .qa or .dev to the bundle identifier in iOS – rajeshzmoke Jun 17 '19 at 08:21
-
1@siva kumar but we cannot build through flutter `flutter build apk --qa` – Saahithyan Vigneswaran Apr 13 '20 at 14:15
-
-
Update July 2019 :
I wrote a Package that integrates the Flutter Global Config.
EZ Flutter is a collection of widgets, packages and many more usefull things, mixed up in little framework. The aim is to make standard features available from scratch.
Github : https://github.com/Ephenodrom/EZ-Flutter
dependencies:
ez_flutter: ^0.2.0
Check out the documentation how using different configurations works.
https://github.com/Ephenodrom/EZ-Flutter/blob/master/documentation/APPLICATION_SETTINGS.md
++++ OLD ANSWER ++++
Additional information :
I had the same problem and used the solution suggested by Seth Ladd. Therefore I also needed different configuration for each app version (dev / prod ) and i don't want to write the configuration in the main_dev.dart or in the main_prod.dart file.
I wrote a simple flutter package that deals with having seperated configuration files and load them at app startup. The configuration is then available at each line of code in your app.
https://github.com/Ephenodrom/Flutter-Global-Config
How to use it :
Create a json file under assets/cfg/$file.json
Add assets/cfg to your pubspec.yaml
Loading different configuration files at app start :
import 'package:flutter/material.dart';
import 'package:global_configuration/global_configuration.dart';
void main() async{
await GlobalConfiguration().loadFromAsset("app_settings");
await GlobalConfiguration().loadFromAsset("env_dev_settings");
runApp(MyApp());
}
class MyApp extends StatelessWidget {
...
}
Using the configuration in your app :
import 'package:flutter/material.dart';
import 'package:global_configuration/global_configuration.dart';
class CustomWidget extends StatelessWidget {
CustomWiget(){
// Access the config in the constructor
print(GlobalConfiguration().getString("key1"); // prints value1
}
@override
Widget build(BuildContext context) {
// Access the config in the build method
return new Text(GlobalConfiguration().getString("key2"));
}
}

- 1,797
- 2
- 16
- 28
Create a file at the root of the project app_environment.dart
. Use the kReleaseMode
variable from foundation.dart
package to check for production mode.
import 'package:flutter/foundation.dart';
class AppEnvironment {
String getApiURL() {
if (kReleaseMode) {
return 'PROD_API_URL';
} else {
return 'STAGING_API_URL';
}
}
}

- 5,370
- 5
- 42
- 65