flutter_dotenv
Load configuration at runtime from a .env
file which can be used throughout the application.
The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code... they are a language- and OS-agnostic standard.
About
This library is a fork of mockturtl/dotenv dart library, initially with slight changes to make it work with flutter.
An environment is the set of variables known to a process (say, PATH
, PORT
, ...).
It is desirable to mimic the production environment during development (testing,
staging, ...) by reading these values from a file.
This library parses that file and merges its values with the built-in
Platform.environment
map.
Security Considerations
Sensitive keys like API keys and tokens should not be stored in your Flutter app. They can be extracted even if obfuscated. This libary currently does not obfuscate variables as it may lull the consumers into a false sense of security. Use environment variables on the frontend application for non-sensitive configuration values, such as API endpoints and feature flags.
For more details on mobile app security best practices, refer to the OWASP Mobile Security Project.
Usage
- Create a
.env
file in the root of your project with the example content:
FOO=foo
BAR=bar
FOOBAR=$FOO$BAR
ESCAPED_DOLLAR_SIGN='$1000'
# This is a comment
Note: If deploying to web server, ensure that the config file is uploaded and not ignored. (Whitelist the config file on the server, or name the config file without a leading
.
)
- Add the
.env
file to your assets bundle inpubspec.yaml
. Ensure that the path corresponds to the location of the .env file!
assets:
- .env
- Remember to add the
.env
file as an entry in your.gitignore
if it isn't already unless you want it included in your version control.
*.env
- Load the
.env
file inmain.dart
. Note thatflutter_dotenv >=5.0.0
has a slightly different syntax for consuming the DotEnv data.
v5.0.0 and later
import 'package:flutter_dotenv/flutter_dotenv.dart';
// DotEnv dotenv = DotEnv() is automatically called during import.
// If you want to load multiple dotenv files or name your dotenv object differently, you can do the following and import the singleton into the relavant files:
// DotEnv another_dotenv = DotEnv()
Future main() async {
// To load the .env file contents into dotenv.
// NOTE: fileName defaults to .env and can be omitted in this case.
// Ensure that the filename corresponds to the path in step 1 and 2.
await dotenv.load(fileName: ".env");
//...runapp
}
You can then access variables from .env
throughout the application
import 'package:flutter_dotenv/flutter_dotenv.dart';
dotenv.env['VAR_NAME'];
Before v5.0.0
import 'package:flutter_dotenv/flutter_dotenv.dart' as DotEnv;
Future main() async {
await DotEnv.load(fileName: ".env");
//...runapp
}
Access env using:
import 'package:flutter_dotenv/flutter_dotenv.dart';
env['VAR_NAME'];
Optionally you could map env
after load to a config model to access a config with types.
Advanced usage
Refer to the test/dotenv_test.dart
file for a better idea of the behaviour of the .env
parser.
Get with typing
It's a common pattern to get an env variable then parse it as a type. You can get variables that are already typed by using the following functions:
// someBool is a bool
final someBool = dotenv.getBool('VAR_NAME', fallback: true);
// someDouble is a double
final someDouble = dotenv.getDouble('VAR_NAME', fallback: .3);
// someInt is an int
final someInt = dotenv.getInt('VAR_NAME', fallback: 42);
Referencing
You can reference variables defined above other within .env
:
FOO=foo
BAR=bar
FOOBAR=$FOO$BAR
You can escape referencing by wrapping the value in single quotes:
ESCAPED_DOLLAR_SIGN='$1000'
Merging
You can merge a map into the environment on load:
await DotEnv.load(mergeWith: { "FOO": "foo", "BAR": "bar"});
You can also reference these merged variables within .env
:
FOOBAR=$FOO$BAR
Using in tests
There is a testLoad
method that can be used to load a static set of variables for testing.
// Loading from a static string.
dotenv.testLoad(fileInput: '''FOO=foo
BAR=bar
''');
// Loading from a file synchronously.
dotenv.testLoad(fileInput: File('test/.env').readAsStringSync());
Null safety
To avoid null-safety checks for variables that are known to exist, there is a get()
method that
will throw an exception if the variable is undefined. You can also specify a default fallback
value for when the variable is undefined in the .env file.
Future<void> main() async {
await dotenv.load();
String foo = dotenv.get('VAR_NAME');
// Or with fallback.
String bar = dotenv.get('MISSING_VAR_NAME', fallback: 'sane-default');
// This would return null.
String? baz = dotenv.maybeGet('MISSING_VAR_NAME', fallback: null);
}
Usage with Platform Environment
The Platform.environment map can be merged into the env:
// For example using Platform.environment that contains a CLIENT_ID entry
await DotEnv.load(mergeWith: Platform.environment);
print(env["CLIENT_ID"]);
Like other merged entries described above, .env
entries can reference these merged Platform.Environment entries if required:
CLIENT_URL=https://$CLIENT_ID.dev.domain.com
Security Considerations
Never store sensitive keys in the frontend:
Sensitive keys like API keys and tokens should not be stored in your Flutter app. They can be extracted even if obfuscated. This libary currently chooses not to ocfuscate the variables as it may lull the consumers into a false sense of security. Use environment variables for non-sensitive configuration values, such as API endpoints and feature flags.
Discussion
Use the issue tracker for bug reports and feature requests.
Pull requests are welcome.
Prior art
- mockturtl/dotenv (dart)
- bkeepers/dotenv (ruby)
- motdotla/dotenv (node)
- theskumar/python-dotenv (python)
- joho/godotenv (go)
- slapresta/rust-dotenv (rust)
- chandu/dotenv (c#)
- tpope/lein-dotenv, rentpath/clj-dotenv (clojure)
- mefellows/sbt-dotenv (scala)
- greenspun/dotenv (half of common lisp)