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 easil
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
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
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());
}
env.dart
to expose the env
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.
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
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"));
}
}
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';
}
}
}