matanlurey
matanlurey

Reputation: 8624

Setting environment variables in Flutter

For example, building a client for an API, like Twitch.

In a Dart CLI binary, I could use a generic environment variable, or a Dart definition variable. For example, using both as fallbacks:

main() {
  String clientId = 
      // dart -dCLIENT_ID='abc bin/example.dart
      // This is considered "compiled-into" the application.
      const String.fromEnvironment('CLIENT_ID') ??

      // CLIENT_ID='abc' dart bin/example.dart
      // This is considered a runtime flag.
      Platform.environment['CLIENT_ID'];

  // Use clientId.
}

Does Flutter have a way of setting either/both of these, specifically...

Happy to help with some docs once I figure out how :)

Upvotes: 120

Views: 158194

Answers (11)

Hossein Sajadinia
Hossein Sajadinia

Reputation: 594

If you're using the Flutter version >= 3.7 you can pass the environment variables in 2 ways either by argument or by a config file. for example:

flutter run --dart-define=BASE_URL=http://localhost:3000

Or you can create a file such as env.json and set your all desired variables in it for example:

{
  "BASE_URL": "http://localhost:3000",
  "TEST_USER": "test_user"
}

and then pass the file:

flutter run --dart-define-from-file=env.json

And if your Flutter version is < 3.7 you have only the first option

Upvotes: 13

Codefarmer
Codefarmer

Reputation: 1533

Flutter introduced environment variables at compile-time using the --dart-define argument. Where you have more than one environment variable, using the --dart-define-from-file argument is advisable.

For a single environment variable, follow the below:

flutter run --dart-define=VAR_NAME=SOME_VALUE

Where there is more than one environment variable, follow the below steps:

  • Create a JSON file containing the variables.
{
    "VAR_A": someValue,
    "VAR_B": anotherValue
}
  • pass this file to the flutter build/run command
flutter run --dart-define-from-file=config.json

where config.json is the created JSON file containing the variables.

To retrieve these variables from your code, any of the following works depending on the data type:

const VAR_A = String.fromEnvironment('VAR_A', defaultValue: 'SOME_DEFAULT_VALUE');
const VAR_B = int.fromEnvironment('VAR_B', defaultValue: 1);
const VAR_C = bool.fromEnvironment('VAR_C', defaultValue: false);

It should be noted that there are no fromEnvironment constructors for double. The above arguments can also be used with the flutter build command.

The article here explains how to do this in-depth.

Upvotes: 6

JohnIsowhiz
JohnIsowhiz

Reputation: 179

If you have multiple environment variables, use option --dart-define-from-file=env.json.

Ex:

flutter build web --dart-define-from-file=env.json

or

flutter run --dart-define-from-file=env_dev.json

Place env.json in the root, where pubspec.yaml resides.

Sample json files

  1. env.json

    { "backend_url": "https://server.com" }

  2. env_dev.json

    { "backend_url": "https://dev.server.com" }

Sample use:

const backendUrl = String.fromEnvironment('backend_url', defaultValue: 'SOME_DEFAULT_VALUE');

Upvotes: 17

Chintan Puggalok
Chintan Puggalok

Reputation: 1

although above answers are correct coming from python and reactjs I used dotenv and found the same for flutter to load .env file https://pub.dev/packages/dotenv

Upvotes: 0

Samuel Nde
Samuel Nde

Reputation: 2753

I do agree with the answer posted by @tatsuDn but I wanted to provide a solution that loads your environment variables from a .env file.

First create a .env file in the root folder of your project.
Ensure that you add the file to your pubspec.yaml and [git] ignore it.

Here is how your .env file should look

API_KEY=sampleapikey
# This line is a comment

# The white line above will be ignored
HEADER=sampleapiheader
ANOTHER_UNIQUE_KEY=theValueOfThisKey
KEY_CONTAINS_#=*234*5#
KEY_CONTAINS_EQUALS=IP8iwe=0&

Here is how your assets section to look like.

# To add assets to your application, add an assets section, like this:
assets:
  - assets/images/
  - assets/flags/
  - .env

Finally, load your environment variable by reading and parsing the .env file to get a Map<String, String> that contains your key value pairs.

Future<Map<String, String>> parseStringToMap({String assetsFileName = '.env'}) async {
  final lines = await rootBundle.loadString(assetsFileName);
  Map<String, String> environment = {};
  for (String line in lines.split('\n')) {
    line = line.trim();
    if (line.contains('=') //Set Key Value Pairs on lines separated by =
        &&
        !line.startsWith(RegExp(r'=|#'))) {
      //No need to add emty keys and remove comments
      List<String> contents = line.split('=');
      environment[contents[0]] = contents.sublist(1).join('=');
    }
  }
  return environment;
}

You can put a quick button in your code to test that the environment variables are being loaded properly.

ElevatedButton(
    onPressed: () async {
      final env = await parseStringToMap(assetsFileName: '.env');
      print(env);
    },
    child: Text('Print Environment Variables')
),

Here is the output from the .env file above.

>>>I/flutter ( 7182): {API_KEY: sampleapikey, HEADER: sampleapiheader, ANOTHER_UNIQUE_KEY: theValueOfThisKey, KEY_CONTAINS_#: *234*5#, KEY_CONTAINS_EQUALS: IP8iwe=0&}

Notes: You will need to rerun the app (not hot reload) so that the .env assets is loaded.
You can also just load your variables in a json file[this may be helpful when you have non string environemental variables and you dont want to parse string.
To avaoid IO, it is a good Idea to just load the environment variables once and access them through out the app using service locators like GetIt.

Upvotes: 7

Ben Butterworth
Ben Butterworth

Reputation: 29038

To run your app (in flutter run)

  • flutter run --dart-define=EXAMPLE_API_ENDPOINT=https://api.example.com/

To release your app (in flutter build)

My app wasn't letting users log in I realized that environment variables were empty strings in the app, instead of their actual values šŸ˜….

  • iOS: flutter build ipa --dart-define=EXAMPLE_API_ENDPOINT=https://api.example.com/
  • Android: flutter build apk --dart-define=EXAMPLE_API_ENDPOINT=https://api.example.com/

--dart-define documentation

From the flutter run --help or flutter build ipa --help, the --dart-define shows:

Additional key-value pairs that will be available as 
constants from the String.fromEnvironment, bool.fromEnvironment, 
int.fromEnvironment, and double.fromEnvironment constructors. 
Multiple defines can be passed by repeating "--dart-define" 
multiple times.

Upvotes: 17

UdaraWanasinghe
UdaraWanasinghe

Reputation: 2882

I use simple shell script to generate dart defines. In my app there are 3 build flavors: dev, staging and prod. Environment variables were defined in a regular .env file.

env/
ā”œā”€ā”€ dev.env
ā”œā”€ā”€ prod.env
ā””ā”€ā”€ staging.env

Here is the script to generate dart-defines from .env file.

#!/bin/bash

# scripts/generate_dart_defines.sh

case "$1" in
"dev") INPUT="env/dev.env"
;;
"staging") INPUT="env/staging.env"
;;
"prod") INPUT="env/prod.env"
;;
*)
  echo "Missing arguments [dev|staging|prod]"
  exit 1
;;
esac

while IFS= read -r line
do
  DART_DEFINES="$DART_DEFINES--dart-define=$line "
done < "$INPUT"
echo "$DART_DEFINES"

Here is the script to trigger a build.

#!/bin/bash

# build.sh

if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
  echo -e "Missing arguments: [apk|appbundle|ios] [release|debug|profile] [dev|staging|prod]"
  # invalid arguments
  exit 128
fi

DART_DEFINES=$(scripts/generate_dart_defines.sh $3)

if [ $? -ne 0 ]; then
  echo -e "Failed to generate dart defines"
  exit 1
fi

echo -e "artifact: $1, type: $2, flavor: $3\n"
echo -e "DART_DEFINES: $DART_DEFINES\n"

eval "flutter build $1 --$2 --flavor $3 $DART_DEFINES"

The script accepts 3 arguments. First one is the artifact apk, appbundle or ios. Second one is the build type release, debug or profile. Third one is the build flavor, dev, staging or prod.

./build.sh apk release prod

Please note that you also required to configure android and ios for different build flavors separately. https://developer.android.com/studio/build/build-variants

https://shockoe.com/ideas/development/how-to-setup-configurations-and-schemes-in-xcode/

https://developer.apple.com/library/archive/documentation/ToolsLanguages/Conceptual/Xcode_Overview/ManagingSchemes.html

Upvotes: 15

tatsuDn
tatsuDn

Reputation: 2439

Starting from Flutter 1.17 you can define compile-time variables if you want to.

To do so just use --dart-define argument during flutter run or flutter build

If you need to pass multiple key-value pairs, just define --dart-define multiple times:

flutter run --dart-define=SOME_VAR=SOME_VALUE --dart-define=OTHER_VAR=OTHER_VALUE

and then, anywhere in your code you can use them like:

const SOME_VAR = String.fromEnvironment('SOME_VAR', defaultValue: 'SOME_DEFAULT_VALUE');
const OTHER_VAR = String.fromEnvironment('OTHER_VAR', defaultValue: 'OTHER_DEFAULT_VALUE');

Also, they can be used in native layers too.

Here is an article that explains more.

Upvotes: 175

CopsOnRoad
CopsOnRoad

Reputation: 268534

Create a class:

import 'package:flutter/foundation.dart';

class AppUtils {
  static String get clientId {
    if (kDebugMode) return 'debug_id';
    else if (kProfileMode) return 'profile_id';
    else if (kReleaseMode) return 'production_id';
    else if (kIsWeb) return 'web_mode_id';
    
    throw ArgumentError('No mode detected');
  }
}

Usage:

var id = AppUtils.clientId;

Upvotes: -4

Oreflow
Oreflow

Reputation: 607

Since I was trying to solve this as well and encountered this thread I just wanted to add this for people looking for a solution in the future... If all you're looking for is PROD/DEV environments there is now a supported way of getting if the app is in production or not:

const bool isProduction = bool.fromEnvironment('dart.vm.product');

As suggested by:

https://twitter.com/FlutterDev/status/1048278525432791041

https://github.com/flutter/flutter/issues/4014

Upvotes: 32

Eric Seidel
Eric Seidel

Reputation: 3522

For configuration a common pattern I've seen is to use separate main files instead. i.e.

flutter run -t lib/production_main.dart

and

flutter build apk -t lib/debug_main.dart

And then in those different main files set up the configurations desired.

In terms of reading ids, you can do that from arbitrary assets https://flutter.io/assets-and-images/.

I believe it is possible in Flutter to read from the environment as you suggest, however I don't know how to set those environment variables on iOS or Android.

Upvotes: 37

Related Questions