|
| 1 | +# flutter_dotenv |
| 2 | + |
| 3 | +flutter_dotenv is a Flutter/Dart package that loads configuration at runtime from a `.env` file which can be used throughout the application. This follows the twelve-factor app methodology for configuration management. |
| 4 | + |
| 5 | +Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here. |
| 6 | + |
| 7 | +## Working Effectively |
| 8 | + |
| 9 | +### Prerequisites and Environment Setup |
| 10 | +- Install Flutter SDK (stable channel recommended): |
| 11 | + ```bash |
| 12 | + # Option 1: Using snap (Linux) |
| 13 | + sudo snap install flutter --classic |
| 14 | + |
| 15 | + # Option 2: Manual installation |
| 16 | + git clone https://github.com/flutter/flutter.git -b stable |
| 17 | + export PATH="$PATH:`pwd`/flutter/bin" |
| 18 | + ``` |
| 19 | +- Verify installation: `flutter doctor` |
| 20 | +- Ensure Dart SDK is included with Flutter (no separate installation needed) |
| 21 | + |
| 22 | +### Bootstrap, Build, and Test Repository |
| 23 | +**CRITICAL TIMING**: All commands below have specific timeout requirements. NEVER CANCEL these operations. |
| 24 | + |
| 25 | +```bash |
| 26 | +# Navigate to repository |
| 27 | +cd /home/runner/work/flutter_dotenv/flutter_dotenv |
| 28 | + |
| 29 | +# Get dependencies - takes 30-90 seconds. NEVER CANCEL. Set timeout to 120+ seconds. |
| 30 | +flutter pub get |
| 31 | + |
| 32 | +# Run static analysis - takes 10-30 seconds. NEVER CANCEL. Set timeout to 60+ seconds. |
| 33 | +flutter analyze |
| 34 | + |
| 35 | +# Run tests with coverage - takes 30-60 seconds. NEVER CANCEL. Set timeout to 120+ seconds. |
| 36 | +flutter test --coverage |
| 37 | + |
| 38 | +# Format code (if making changes) - takes 5-15 seconds. NEVER CANCEL. Set timeout to 30+ seconds. |
| 39 | +dart format lib/ test/ example/lib/ |
| 40 | +``` |
| 41 | + |
| 42 | +### Development Workflow |
| 43 | +- **ALWAYS run `flutter pub get` first** after cloning or when dependencies change |
| 44 | +- **ALWAYS run `flutter analyze`** before making changes to understand current state |
| 45 | +- **ALWAYS run `flutter test`** to verify functionality |
| 46 | +- **ALWAYS run `dart format`** before committing (CI will fail otherwise) |
| 47 | + |
| 48 | +### Running the Example Application |
| 49 | +- Navigate to example directory: `cd example/` |
| 50 | +- Get example dependencies: `flutter pub get` - takes 30-90 seconds. NEVER CANCEL. Set timeout to 120+ seconds. |
| 51 | +- Run example app: `flutter run` - takes 60-180 seconds to build and launch. NEVER CANCEL. Set timeout to 300+ seconds. |
| 52 | +- For web: `flutter run -d chrome` |
| 53 | +- Note: Example demonstrates loading .env files with environment variables |
| 54 | + |
| 55 | +## Validation |
| 56 | + |
| 57 | +### Manual Testing Scenarios |
| 58 | +**CRITICAL**: After making ANY changes, ALWAYS run through these validation scenarios: |
| 59 | + |
| 60 | +1. **Basic .env Loading Test**: |
| 61 | + ```bash |
| 62 | + cd example/ |
| 63 | + flutter run |
| 64 | + # Verify the app loads and displays environment variables from assets/.env |
| 65 | + # Check that FOO=foo, BAR=bar, and FOOBAR=\$FOOfoobar display correctly |
| 66 | + # Verify ESCAPED_DOLLAR_SIGN shows "\$1000" properly |
| 67 | + ``` |
| 68 | + |
| 69 | +2. **Parser Functionality Test**: |
| 70 | + ```bash |
| 71 | + flutter test test/parser_test.dart --verbose |
| 72 | + # Verify all parser tests pass: |
| 73 | + # - Variable substitution (\$FOO${FOO} patterns) |
| 74 | + # - Quote handling (single/double quotes, nested quotes) |
| 75 | + # - Comment parsing (# comments are ignored) |
| 76 | + # - Equal signs in values (foo=bar=baz handling) |
| 77 | + # - Leading export keyword removal |
| 78 | + ``` |
| 79 | + |
| 80 | +3. **DotEnv Integration Test**: |
| 81 | + ```bash |
| 82 | + flutter test test/dotenv_test.dart --verbose |
| 83 | + # Verify core functionality: |
| 84 | + # - Environment loading with loadFromString() |
| 85 | + # - Fallback values work (get() with fallback parameter) |
| 86 | + # - Type conversions (getInt, getBool, getDouble) |
| 87 | + # - Null safety (maybeGet returns null for missing keys) |
| 88 | + ``` |
| 89 | + |
| 90 | +4. **Empty Environment Test**: |
| 91 | + ```bash |
| 92 | + flutter test test/empty_env_test.dart --verbose |
| 93 | + # Verify edge cases when no .env file exists |
| 94 | + # Ensures graceful fallback behavior |
| 95 | + ``` |
| 96 | + |
| 97 | +5. **Override and Merging Test**: |
| 98 | + ```bash |
| 99 | + # Check that .env-override functionality works in tests |
| 100 | + grep -r "override" test/ example/ |
| 101 | + # Verify overrideWith and mergeWith parameters work correctly |
| 102 | + ``` |
| 103 | + |
| 104 | +### CI/CD Validation |
| 105 | +- **ALWAYS run `flutter analyze`** - CI uses this for static analysis |
| 106 | +- **ALWAYS run `flutter test --coverage`** - CI requires test coverage |
| 107 | +- **ALWAYS format code with `dart format`** - CI checks formatting |
| 108 | +- **Check analysis_options.yaml compliance** - project uses flutter_lints |
| 109 | + |
| 110 | +## Common Tasks |
| 111 | + |
| 112 | +### Repository Structure |
| 113 | +``` |
| 114 | +flutter_dotenv/ |
| 115 | +├── lib/ |
| 116 | +│ ├── flutter_dotenv.dart # Main library export |
| 117 | +│ └── src/ |
| 118 | +│ ├── dotenv.dart # Core DotEnv implementation |
| 119 | +│ ├── parser.dart # .env file parser |
| 120 | +│ └── errors.dart # Error definitions |
| 121 | +├── test/ |
| 122 | +│ ├── dotenv_test.dart # Main functionality tests |
| 123 | +│ ├── parser_test.dart # Parser-specific tests |
| 124 | +│ ├── empty_env_test.dart # Edge case tests |
| 125 | +│ ├── .env # Test environment file |
| 126 | +│ └── .env-override # Test override file |
| 127 | +├── example/ # Complete Flutter example app |
| 128 | +├── tool/ # Development utilities |
| 129 | +│ ├── fmt.sh # Code formatting script |
| 130 | +│ ├── docs.sh # Documentation generation |
| 131 | +│ └── release.sh # Release automation |
| 132 | +└── .github/workflows/ # CI/CD automation |
| 133 | + └── flutter-tests.yml # Test pipeline |
| 134 | +``` |
| 135 | + |
| 136 | +### Key Files to Monitor |
| 137 | +- **Always check `lib/src/dotenv.dart`** when modifying core functionality |
| 138 | +- **Always check `lib/src/parser.dart`** when modifying .env parsing logic |
| 139 | +- **Always update tests in `test/`** when adding new features |
| 140 | +- **Always check `example/lib/main.dart`** for usage patterns |
| 141 | +- **Always check `pubspec.yaml`** when modifying dependencies |
| 142 | + |
| 143 | +### Development Tools |
| 144 | +```bash |
| 145 | +# Format code (modern command - tool/fmt.sh uses deprecated dartfmt) |
| 146 | +dart format lib/ test/ example/lib/ |
| 147 | + |
| 148 | +# Generate documentation (modern command - tool/docs.sh uses deprecated dartdoc) |
| 149 | +dart doc |
| 150 | +# Documentation will be in doc/api/index.html |
| 151 | + |
| 152 | +# Release (tool/release.sh) - requires GH_KEY_ID environment variable |
| 153 | +# Only use for official releases |
| 154 | +``` |
| 155 | + |
| 156 | +### Common Debugging Steps |
| 157 | +1. **If `flutter pub get` fails**: Check internet connectivity and pubspec.yaml syntax |
| 158 | +2. **If tests fail**: Check test/.env files exist and have correct content |
| 159 | +3. **If example app crashes**: Verify assets/.env files are properly declared in example/pubspec.yaml under assets section |
| 160 | +4. **If analysis fails**: Check analysis_options.yaml and fix linting errors |
| 161 | +5. **If build fails**: Ensure Flutter SDK version matches environment constraints (>=2.12.0-0 <4.0.0) |
| 162 | +6. **If .env parsing fails**: Check for proper quotes, escape sequences, and variable substitution syntax |
| 163 | +7. **If environment variables are null**: Verify .env file is in assets bundle and loadFromString() or load() was called |
| 164 | +8. **If variable substitution doesn't work**: Check test/.env for examples - use \$VAR or \${VAR} syntax |
| 165 | + |
| 166 | +### Performance Expectations |
| 167 | +- **Dependency resolution**: 30-90 seconds for `flutter pub get` |
| 168 | +- **Static analysis**: 10-30 seconds for `flutter analyze` |
| 169 | +- **Test execution**: 30-60 seconds for `flutter test` |
| 170 | +- **Example app build**: 60-180 seconds for first `flutter run` |
| 171 | +- **Documentation generation**: 30-60 seconds for `dart doc` |
| 172 | + |
| 173 | +**CRITICAL**: NEVER CANCEL these operations. They may appear to hang but are processing. Always set timeouts 2-3x the expected time. |
| 174 | + |
| 175 | +### Critical Warnings and Common Pitfalls |
| 176 | + |
| 177 | +**DO NOT**: |
| 178 | +- Cancel `flutter pub get`, `flutter test`, or `flutter run` commands - they take significant time |
| 179 | +- Use deprecated `dartfmt` command (use `dart format` instead) |
| 180 | +- Use deprecated `dartdoc` command (use `dart doc` instead) |
| 181 | +- Forget to declare .env files in pubspec.yaml assets section |
| 182 | +- Put .env files in version control if they contain secrets (check .gitignore) |
| 183 | +- Skip the `await dotenv.load()` call in main() - variables will be empty |
| 184 | + |
| 185 | +**ALWAYS DO**: |
| 186 | +- Set timeouts 2-3x longer than expected completion time |
| 187 | +- Run `flutter pub get` after any pubspec.yaml changes |
| 188 | +- Check that .env files exist in the correct paths (assets/ for example, test/ for tests) |
| 189 | +- Validate .env syntax - no spaces around = signs, proper quotes for special characters |
| 190 | +- Test both with and without .env files (use empty_env_test.dart as reference) |
| 191 | + |
| 192 | +### Package Usage Patterns |
| 193 | +```dart |
| 194 | +// Basic usage pattern |
| 195 | +import 'package:flutter_dotenv/flutter_dotenv.dart'; |
| 196 | +
|
| 197 | +// In main.dart - basic loading |
| 198 | +await dotenv.load(fileName: "assets/.env"); |
| 199 | +
|
| 200 | +// Advanced loading with merging and overrides |
| 201 | +await dotenv.load( |
| 202 | + fileName: "assets/.env", |
| 203 | + mergeWith: { |
| 204 | + 'TEST_VAR': '5', |
| 205 | + 'DEFAULT_SETTING': 'value', |
| 206 | + }, |
| 207 | + overrideWithFiles: ["assets/.env.override"], |
| 208 | +); |
| 209 | +
|
| 210 | +// Access variables |
| 211 | +String apiKey = dotenv.env['API_KEY'] ?? 'default_key'; |
| 212 | +String dbUrl = dotenv.get('DATABASE_URL', fallback: 'localhost'); |
| 213 | +
|
| 214 | +// Null-safe access |
| 215 | +String? optionalValue = dotenv.maybeGet('OPTIONAL_KEY'); |
| 216 | +String valueWithFallback = dotenv.maybeGet('OPTIONAL_KEY', fallback: 'default') ?? 'fallback'; |
| 217 | +
|
| 218 | +// Type conversions |
| 219 | +int port = dotenv.getInt('PORT', fallback: 8080); |
| 220 | +bool debug = dotenv.getBool('DEBUG', fallback: false); |
| 221 | +double timeout = dotenv.getDouble('TIMEOUT', fallback: 30.0); |
| 222 | +
|
| 223 | +// For testing - load from string |
| 224 | +dotenv.loadFromString(envString: File('test/.env').readAsStringSync()); |
| 225 | +``` |
| 226 | + |
| 227 | +### Testing Patterns |
| 228 | +- **Always test with and without .env files** (see empty_env_test.dart) |
| 229 | +- **Always test variable substitution** (FOO=$BAR patterns) |
| 230 | +- **Always test override scenarios** (mergeWith and overrideWith parameters) |
| 231 | +- **Always test type conversions** (getInt, getBool, getDouble) |
| 232 | +- **Always test fallback values** for missing variables |
0 commit comments