Using Environment Variables in Flutter Apps

When developing Flutter applications, especially those interacting with APIs, storing sensitive information such as API keys directly in your source code is highly discouraged. This poses a security risk as these keys can be exposed if your code is committed to a public repository or if someone gains access to your codebase. Instead, utilizing environment variables is a more secure and efficient approach. Environment variables are external configurations that can be accessed by your application at runtime. In this comprehensive guide, we’ll explore how to effectively use environment variables in your Flutter apps to manage configurations and sensitive data securely.

Why Use Environment Variables?

  • Security: Keeps sensitive data out of your codebase, reducing the risk of exposure.
  • Configuration Management: Allows different configurations for different environments (e.g., development, staging, production).
  • Flexibility: Simplifies changing configurations without modifying and redeploying the application.

Methods to Use Environment Variables in Flutter

There are several ways to use environment variables in Flutter. We will cover two common and effective methods: using the flutter_dotenv package and using platform-specific environment variables.

Method 1: Using the flutter_dotenv Package

The flutter_dotenv package allows you to load environment variables from a .env file into your Flutter app. This is a straightforward and popular method for managing configurations.

Step 1: Add the flutter_dotenv Dependency

First, add flutter_dotenv to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  flutter_dotenv: ^5.2.0

Run flutter pub get to install the package.

Step 2: Create a .env File

Create a .env file in the root directory of your Flutter project and add your environment variables:

API_KEY=YOUR_API_KEY
BASE_URL=https://api.example.com
Step 3: Load Environment Variables

Load the environment variables in your main.dart file using dotenv.load(). It’s important to load the environment variables before running the app.

import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

void main() async {
  await dotenv.load(fileName: ".env");
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Env Vars',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Env Vars'),
      ),
      body: Center(
        child: Text(
          'API Key: ${dotenv.env['API_KEY'] ?? 'API Key not found'}n'
          'Base URL: ${dotenv.env['BASE_URL'] ?? 'Base URL not found'}',
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}
Step 4: Access Environment Variables

You can access the environment variables using dotenv.env['VAR_NAME']. Use a null-aware operator (??) to provide a default value in case the variable is not found.

String apiKey = dotenv.env['API_KEY'] ?? 'DEFAULT_API_KEY';
String baseUrl = dotenv.env['BASE_URL'] ?? 'https://default.example.com';
Step 5: Configure .gitignore

To ensure your .env file is not committed to your Git repository, add it to your .gitignore file:

.env

Method 2: Using Platform-Specific Environment Variables

Another approach is to use platform-specific environment variables. This involves setting environment variables directly on the operating system or within your IDE, which can be accessed via the Platform class in Flutter.

Step 1: Set Environment Variables

Set the environment variables on your system. For example:

On macOS/Linux:
export API_KEY=YOUR_API_KEY
export BASE_URL=https://api.example.com

You can add these lines to your shell configuration file (e.g., .bashrc, .zshrc) to make them persistent.

On Windows:

You can set environment variables via the Command Prompt or PowerShell:

[Environment]::SetEnvironmentVariable('API_KEY', 'YOUR_API_KEY', 'User')
[Environment]::SetEnvironmentVariable('BASE_URL', 'https://api.example.com', 'User')

Alternatively, you can set them through the System Properties dialog.

Step 2: Access Environment Variables in Flutter

Access these environment variables using the Platform.environment map:

import 'dart:io';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Env Vars',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Env Vars'),
      ),
      body: Center(
        child: Text(
          'API Key: ${Platform.environment['API_KEY'] ?? 'API Key not found'}n'
          'Base URL: ${Platform.environment['BASE_URL'] ?? 'Base URL not found'}',
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}

Best Practices for Using Environment Variables

  • Keep .env File Separate: Ensure the .env file is not committed to your repository by adding it to .gitignore.
  • Provide Default Values: Always provide default values for environment variables in your code using the null-aware operator (??) to prevent runtime errors.
  • Use Different Environments: Configure different .env files or system environment variables for different environments (development, staging, production).
  • Avoid Sensitive Data in Public Repositories: Never commit sensitive data, even if it’s commented out or obfuscated.

Advanced Configurations

For more complex projects, you may need to manage multiple environment files or configurations based on different build flavors. Here are a few strategies:

Multiple .env Files

You can use different .env files for different environments and load the appropriate file based on a command-line argument or build configuration. For example:

Future main() async {
  String envFile = '.env';
  
  // Example conditional loading based on a build argument
  if (const String.fromEnvironment('FLAVOR') == 'staging') {
    envFile = '.env.staging';
  }

  await dotenv.load(fileName: envFile);
  runApp(MyApp());
}

Then, run your app with:

flutter run --dart-define=FLAVOR=staging

Using Build Flavors

Flutter build flavors allow you to manage different build configurations, such as development, staging, and production. You can set environment variables specific to each flavor.

First, configure flavors in your android/app/build.gradle file:

android {
    flavorDimensions += "environment"
    productFlavors {
        development {
            dimension "environment"
            buildConfigField "String", "API_KEY", '"DEV_API_KEY"'
            buildConfigField "String", "BASE_URL", '"https://dev.example.com"'
        }
        staging {
            dimension "environment"
            buildConfigField "String", "API_KEY", '"STAGING_API_KEY"'
            buildConfigField "String", "BASE_URL", '"https://staging.example.com"'
        }
        production {
            dimension "environment"
            buildConfigField "String", "API_KEY", '"PROD_API_KEY"'
            buildConfigField "String", "BASE_URL", '"https://api.example.com"'
        }
    }
}

Then, access these variables in your Flutter code:

import 'package:flutter/material.dart';
import 'package:your_app/BuildConfig.dart'; // Generated file

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Env Vars',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Env Vars'),
      ),
      body: Center(
        child: Text(
          'API Key: ${BuildConfig.API_KEY}n'
          'Base URL: ${BuildConfig.BASE_URL}',
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}

Conclusion

Using environment variables in Flutter apps is essential for managing sensitive information and configuring different environments securely. Whether you choose to use the flutter_dotenv package or platform-specific environment variables, following best practices will help protect your application and simplify configuration management. Understanding and implementing these techniques ensures your Flutter apps are robust, secure, and easily adaptable to various deployment scenarios.