envified 2.0.2
envified: ^2.0.2 copied to clipboard
Runtime environment switching for Flutter. Load .env files, switch dev/staging/prod/custom at runtime, override base URLs, and lock production config — no rebuild required.
🌿 envified #
Stop Rebuilding. Start Switching. 🚀 #
envified is the runtime brain for your Flutter app. Load your .env files, swap environments on the fly, override API URLs, authenticate access with a PIN or biometric, detect tampering, and keep a full audit trail — all without a single hot reload.
📸 The "Look Ma, No Rebuilds!" UI #
envified ships with a premium, dark-luxury debug overlay. It stays invisible in production but pops up when you need it most.
[envified Floating Button] [envified Debug Panel]
✨ What's New in v2.0.0 #
| # | Feature | Summary |
|---|---|---|
| 1 | Tamper Detection | SHA-256 hash every .env* file on first load; throw EnvifiedTamperException if modified |
| 2 | Access Gate | PIN dialog or biometric auth before opening the debug panel |
| 3 | Typed Getters | getBool, getInt, getDouble, getUri, getList |
| 4 | Lifecycle Hooks | onBeforeSwitch / onAfterSwitch callbacks in init() |
| 5 | URL History | Last 5 URLs auto-saved; one-tap chips in the debug panel |
| 6 | Status Badge | EnvStatusBadge — colour-coded, pulsing when URL override is active |
| 7 | Gesture Trigger | Tap N times, shake the device, or swipe from the right edge |
| 8 | Audit Log | Encrypted, capped-at-50 activity log; visible in the debug panel |
| + | Auto-lock | Panel closes and re-requires auth when app is backgrounded |
✨ Why You'll Love It #
- ⚡️ Switch in Seconds: Swap from
devtoprodin 0.2 seconds. No compilation, no coffee breaks. - 🔒 The "Safety First" Lock: We lock your
prodenvironment by default. No accidental data deletions here. - 🧪 API Mad Scientist Mode: Override your base URL at runtime. Test against that local tunnel or a specific PR branch instantly.
- 💾 Memory Like an Elephant: Your selections and URL overrides persist across app restarts.
- ⚙️ Ghost in the Machine: The debug UI is stripped out completely in release builds. Zero overhead.
- 🔍 Tamper-Evident: SHA-256 integrity checks catch any
.envfile modification after first launch. - 📋 Full Audit Trail: Every environment switch and URL change is logged securely.
📦 Dependencies #
| Package | Purpose |
|---|---|
flutter_secure_storage |
AES (Android) / Keychain (iOS) encrypted persistence |
local_auth |
Biometric / device-credential authentication |
sensors_plus |
Accelerometer for shake-to-open trigger |
crypto |
SHA-256 hashing for tamper detection |
🛠 Quick Start (30 Seconds) #
1. Grab the Package #
dependencies:
envified: ^2.0.0
2. Toss in your .env files #
Create your .env files and tell Flutter where they are in pubspec.yaml:
flutter:
assets:
- assets/env/.env # Shared defaults
- assets/env/.env.dev # Dev overrides
- assets/env/.env.staging # Staging overrides
- assets/env/.env.prod # Prod overrides
3. Light the Fuse #
Initialize before runApp():
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await EnvConfigService.instance.init(
defaultEnv: Env.dev,
allowProdSwitch: false, // Lock prod for safety!
verifyIntegrity: true, // Tamper detection
onBeforeSwitch: (from, to) async {
debugPrint('Switching: ${from.longLabel} → ${to.longLabel}');
},
onAfterSwitch: (config) {
debugPrint('Now on: ${config.baseUrl}');
},
);
runApp(const MyApp());
}
🪄 The Magic Sauce #
Injecting the Overlay #
Wrap your app using the builder pattern. Configure the trigger and optional gate:
MaterialApp(
builder: (context, child) => EnvifiedOverlay(
service: EnvConfigService.instance,
enabled: kDebugMode, // Only show in debug!
gate: EnvGate(pin: '1234'), // PIN protection
trigger: const EnvTrigger.tap(count: 7), // 7 rapid taps
showFab: true, // Set to false for "stealth mode"
child: child ?? const SizedBox.shrink(),
),
home: const MyAwesomeApp(),
)
Adding the Status Badge #
Display a persistent env indicator anywhere in your UI:
Stack(
children: [
MyApp(),
if (kDebugMode)
EnvStatusBadge(service: EnvConfigService.instance),
],
)
Grabbing Values (Typed) #
final svc = EnvConfigService.instance;
// Raw string
final name = svc.get('APP_NAME');
// Typed helpers
final timeout = svc.getInt('TIMEOUT', fallback: 30);
final isDebug = svc.getBool('DEBUG');
final rate = svc.getDouble('RATE_LIMIT', fallback: 1.0);
final webhook = svc.getUri('WEBHOOK_URL');
final allowHosts = svc.getList('ALLOWED_HOSTS');
🔒 Access Gate #
Protect the debug panel with a PIN or biometrics:
// PIN only
EnvGate(pin: '1234')
// Biometric only (Face ID / fingerprint)
EnvGate(biometric: true)
// Either method works
EnvGate(pin: '1234', biometric: true)
The gate is automatically cleared when the app is backgrounded, so the next open always requires re-authentication.
🎯 Gesture Triggers #
| Trigger | Constructor | Description |
|---|---|---|
| Tap N times | EnvTrigger.tap(count: 7) |
Tap any area 7 times within 800 ms |
| Shake device | EnvTrigger.shake(threshold: 15.0) |
Accelerometer shake (2 s debounce) |
| Edge swipe | EnvTrigger.edgeSwipe(edgeWidth: 20) |
Swipe inward from the right edge |
Stealth Mode: Set showFab: false on EnvifiedOverlay to completely hide the floating 🌿 button, making your chosen trigger the only way to access the debug panel.
🔍 Tamper Detection #
await EnvConfigService.instance.init(
verifyIntegrity: true,
);
On first launch the SHA-256 hash of each .env* file is stored securely. On every subsequent launch the hash is recomputed. If a file has been modified an EnvifiedTamperException is thrown.
📋 Audit Log #
Every mutating action is logged automatically:
final entries = await EnvConfigService.instance.auditLog;
for (final entry in entries) {
print('${entry.timestamp} — ${entry.action}');
// e.g. "2026-05-07T10:30:00Z — switch (dev → staging)"
}
The log is stored in flutter_secure_storage, capped at 50 entries, and the last 10 entries are visible in the EnvDebugPanel.
🔒 Enterprise-Grade Security #
- Encrypted Persistence: Every environment switch and URL override is persisted using AES encryption on Android and the Secure Keychain on iOS.
- Production Lock:
allowProdSwitch: falseprevents leaving production or overriding URLs. - URL Allowlist: Supply
allowedUrls: ['https://api.myapp.com']to reject unexpected base URLs. - Tamper Detection: SHA-256 integrity checks on
.env*files. - Zero-Leak Release: The debug 🌿 button and panel are completely optimized out in release builds.
⚙️ Platform Setup #
local_auth — Biometric Authentication #
Android
Add to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
Also ensure your MainActivity extends FlutterFragmentActivity:
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterFragmentActivity()
iOS
Add to ios/Runner/Info.plist:
<key>NSFaceIDUsageDescription</key>
<string>Used to authenticate access to the envified debug panel.</string>
sensors_plus — Shake Trigger #
No additional setup required. Works out of the box on Android and iOS.
🔄 Migration from v1.0.0 #
All new parameters in init() and EnvifiedOverlay() are optional with safe defaults. Your existing v1.0.0 code will compile and run without changes.
// v1.0.0 — still works unchanged
await EnvConfigService.instance.init(defaultEnv: Env.dev);
EnvifiedOverlay(
service: EnvConfigService.instance,
enabled: kDebugMode,
child: child!,
)
The only breaking change is EnvStorage.clear() now also wipes URL history and the audit log (desired behaviour for a full reset). If you relied on clear() preserving history, use selective deletion instead.
🤝 Contributing (Join the Cult! 🌿) #
Got an idea to make envified even more magical? We love PRs!
- Fork it: Click that button at the top right.
- Branch it:
git checkout -b feature/my-amazing-idea. - Code it: Make your changes (and add tests, or the lint gods will be angry).
- Commit it:
git commit -m 'Add some magic'. - Push it:
git push origin feature/my-amazing-idea. - Open a PR: And wait for the applause. 👏
🐛 Found a Bug? (The "Oh No!" Section) #
If something isn't working right, or you have a feature request that just can't wait:
- Head over to the Issue Tracker.
- Search if someone else already complained about it.
- If not, open a new issue. Be descriptive! "It's broken" helps no one.
📄 License #
MIT. Go build something amazing. 🚀