Hi everyone, in this post, I want to show you the best way to store environment variables in a Flutter project that I started to use some months ago in all of my projects after seeing it in this blog post from CodewithAndrea: How to Store API Keys in Flutter: --dart-define vs .env files.
In an application that communicates with external services, that needs to authenticate via API key or to configure particular settings we must set environment variables that must be kept as hidden as possible, especially those related to API keys, but rather conveniently to be used in our Flutter applications.
For all this, we are helped by the valid ENVied package that lets you create a convenient Dart class that maps the .env
file. To install it we can add it to the pubspec.yaml
file along with envied_generator
and build_runner
for the build step and generation of the Dart class:
dependencies:
...
envied: ^0.3.0
dev_dependencies:
...
envied_generator: ^0.3.0
build_runner: ^2.3.3
Now we can create an .env
file inside our project where we can add all our environment variables like this:
GREETINGS="Hello world!"
MY_NAME="Alberto"
Now we have to create a env.dart
file where we can define our constants:
import 'package:envied/envied.dart';
part 'env.g.dart';
//Here you can specify the path to your .env file
// relative to the project root
@Envied(path: '.env')
abstract class Env {
//The ofuscate flag lets you make your variables more
// secure through obfuscation
@EnviedField(varName: 'GREETINGS', obfuscate: true)
static final greetings = _Env.greetings;
@EnviedField(varName: 'MY_NAME', obfuscate: true)
static final myName = _Env.myName;
}
Now we can do the build step and let ENVied generate the class for us, so let's run:
flutter pub run build_runner build --delete-conflicting-outputs
ENVied will generate a file named env.g.dart
alongside env.dart
with pretty much this content:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'env.dart';
// *****************************************************
// EnviedGenerator
// *****************************************************
class _Env {
static const List<int> _enviedkeygreetings = [
3271495377, 3997315090, 436058207, 3553787292, 3538777736,
955590460, 3819871908, 4039201348, 2424541315, 3686169803,
1299522498, 1263434759
];
static const List<int> _envieddatagreetings = [
3271495321, 3997315191, 436058163, 3553787376, 3538777831,
955590428, 3819871955, 4039201323, 2424541425, 3686169767,
1299522470, 1263434790
];
static final greetings = String.fromCharCodes(
List.generate(_envieddatagreetings.length, (i) => i, growable: false)
.map((i) => _envieddatagreetings[i] ^ _enviedkeygreetings[i])
.toList(growable: false),
);
static const List<int> _enviedkeymyName = [
3026637516, 1908999005, 3526437322, 2478128297, 2753058997,
855764648, 1111676782
];
static const List<int> _envieddatamyName = [
3026637453, 1908998961, 3526437288, 2478128332, 2753059015,
855764700, 1111676673
];
static final myName = String.fromCharCodes(
List.generate(_envieddatamyName.length, (i) => i, growable: false)
.map((i) => _envieddatamyName[i] ^ _enviedkeymyName[i])
.toList(growable: false),
);
}
Before proceeding it is good to exclude from version control the .env
and env.g.dart
files that contain all our variables, to do this simply add to the .gitignore
file:
#Envied
*.env
env.g.dart
App Code
At this point, we can create a small example app that shows how to use our environment variables and call them within the code, so create a main.dart
file with the following code:
import 'package:flutter/material.dart';
import 'env.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'ENVied Demo',
home: Scaffold(
appBar: AppBar(
title: const Text("ENVied Demo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
Env.greetings,
),
Text(
"Hi, I'm ${Env.myName}",
),
],
),
),
),
);
}
}
As you can see we can call within the code of our app the environment variables we created simply with:
//Import the env class
import 'env.dart';
//Use variables
Env.greetings
Env.myName
The result is something like this:
Conclusions
In this post, we saw how to secure our environment variables through obfuscation, how to exclude them from version control so they are not exposed in our repositories, and how to generate a Dart class that we can access throughout the code to conveniently use our variables. So we have all the requirements to start developing an app with a good degree of security.
Code strong, Alberto