20 Env Var Management
Environment variable management in Dart/Flutter is indeed different from Python, and I’ll walk you through the best practices that align with your experience.
20.1 Understanding the Flutter Environment Context
Unlike Python where you can easily load .env
files at runtime, Flutter apps are compiled and deployed to mobile devices or web browsers. This creates different security and access patterns that we need to handle carefully.
20.2 Recommended Approach: Build-Time Configuration
The most secure and widely adopted approach is to inject environment variables at build time rather than runtime. Here’s why this works better for mobile apps:
Security consideration: Since mobile apps are distributed as compiled packages, any secrets embedded in the app bundle can potentially be extracted by determined attackers. This is fundamentally different from server-side Python applications.
20.3 Step-by-Step Implementation
Let me show you how to set this up, building from concepts you already know:
20.3.1 1. Create Environment-Specific Configuration Files
Instead of a single .env
file, create separate configuration files for each environment:
// lib/config/app_config.dart
class AppConfig {
static const String apiBaseUrl = String.fromEnvironment(
'API_BASE_URL',
: 'https://api.example.com',
defaultValue
);
static const String apiToken = String.fromEnvironment(
'API_TOKEN',
: '',
defaultValue
);
static const bool isProduction = bool.fromEnvironment(
'IS_PRODUCTION',
: false,
defaultValue
);}
20.3.2 2. Pass Variables During Build
When building your app, pass the environment variables through the command line:
# For development
flutter run --dart-define=API_BASE_URL=https://dev-api.example.com --dart-define=API_TOKEN=your_dev_token
# For production
flutter build apk --dart-define=API_BASE_URL=https://api.example.com --dart-define=API_TOKEN=your_prod_token --dart-define=IS_PRODUCTION=true
20.3.3 3. Create Build Scripts (Similar to Your Shell Experience)
You can create shell scripts to manage these complex commands, leveraging your zsh knowledge:
#!/bin/zsh
# scripts/build_dev.sh
# Load environment variables from file
source .env.dev
flutter run \
--dart-define=API_BASE_URL=$API_BASE_URL \
--dart-define=API_TOKEN=$API_TOKEN \
--dart-define=IS_PRODUCTION=false
20.4 Alternative: Runtime Configuration for Non-Sensitive Data
For non-sensitive configuration that might change after deployment, you can still use a runtime approach similar to Python:
// lib/config/runtime_config.dart
import 'dart:io';
import 'package:flutter/services.dart';
class RuntimeConfig {
static Map<String, String> _config = {};
static Future<void> loadConfig() async {
try {
// Load from assets (similar to reading a file in Python)
final configString = await rootBundle.loadString('assets/config.json');
final configJson = jsonDecode(configString);
= Map<String, String>.from(configJson);
_config } catch (e) {
'Error loading config: $e');
print(}
}
static String get(String key, {String defaultValue = ''}) {
return _config[key] ?? defaultValue;
}
}
20.5 Connecting to Your Existing Workflow
Since you’re comfortable with Git and already use environment variables in Python, here’s how to adapt your workflow:
1. Git Integration: Add your environment files to .gitignore
just like in Python:
.env
.env.dev
.env.prod
*.env.local
2. Documentation: Create a .env.example
file showing the required variables:
# .env.example
API_BASE_URL=https://api.example.com
API_TOKEN=your_token_here
IS_PRODUCTION=false
3. CI/CD Integration: With your AppVeyor experience, you can set environment variables in your build pipeline, similar to how you might do it for Python deployments.
20.6 Security Best Practices
Think of this approach like the difference between server-side Python (where secrets stay on the server) and client-side JavaScript (where everything is potentially visible). For truly sensitive data like API keys, consider:
- Using Flutter’s platform channels to access native iOS/Android keychain storage
- Implementing a backend proxy that handles sensitive API calls
- Using OAuth flows instead of embedded API tokens
20.7 Questions to Consider
As you implement this, think about: What types of configuration does your app need? Which values are truly secret versus just environment-specific? How will you handle different configurations for development, testing, and production?
Would you like me to elaborate on any of these approaches or help you implement a specific configuration strategy for your project?