envified 2.0.8 copy "envified: ^2.0.8" to clipboard
envified: ^2.0.8 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 #

pub package pub points License: MIT Dart CI Buy me a Chai

Stop rebuilding. Start switching.
Runtime environment magic for Flutter apps. No hot reload needed.


The Problem #

You're a Flutter developer. Every time you need to test a different API endpoint—local dev server, staging, production—you rebuild the app. With --dart-define flags. Or .env files baked into the binary. Or multiple entry points. It's tedious. It's error-prone. It breaks flow.

What if you could swap environments in 0.2 seconds? No rebuild. No compilation. Just tap, tap, done.

That's envified.


What is envified? #

envified is a production-grade environment manager for Flutter that lives entirely at runtime.

  • 🚀 Swap dev ↔ prod in 200ms — no rebuild, no hot reload
  • 🔒 Prod lock by default — prevent accidental data disasters
  • 🧪 Override any URL — test against local tunnels, PR branches, anywhere
  • 🔐 PIN gate — secure the debug panel
  • 📋 Full audit trail — log every switch and URL change
  • ⚙️ Zero production overhead — stripped out completely in release builds
  • 🎨 Premium debug UI — dark-luxury design, fully customizable

It's not just a config switcher. It's enterprise-grade security meets developer quality of life.

Note

Security Note: While envified encrypts the active configuration state and overrides on the device (via Keychain/Keystore), the base .env files stored in your Flutter assets remain plaintext. Never store high-stakes production secrets directly in .env files; they should be fetched at runtime from a secure vault or used for non-sensitive configuration only.


📸 See It In Action #

Live demo: tap to switch


v2.0+ — What's Inside #

Feature What It Does Why You Care
Tamper Detection SHA-256 hashes .env* files; throws if modified Catch rogue config changes on rooted devices
Access Gate PIN dialog before opening panel QA devices don't leak sensitive switches
Typed Getters getBool(), getInt(), getUri(), getList() No more string parsing bugs
Lifecycle Hooks onBeforeSwitch / onAfterSwitch callbacks Flush HTTP queues, log analytics, etc.
URL History Last 5 URLs one-tap available Faster testing against recent tunnels
Status Badge Persistent [DEV] indicator in your app Never forget what env you're testing
Gesture Triggers Tap N times, shake, or swipe edge to open Customize to your preference
Audit Log Encrypted log of every switch (capped 50 entries) "Who changed prod at 3pm?"
Auto-lock Panel closes when app backgrounded Shoulder-surf proof

Quick Start (3 Steps) #

1️⃣ Install #

dependencies:
  envified: ^2.0.8

2️⃣ Add .env Files #

Create in assets/env/:

# .env (shared across all envs)
APP_NAME=MyApp
TIMEOUT=30

# .env.dev
BASE_URL=https://dev.api.myapp.com
DEBUG=true

# .env.staging
BASE_URL=https://staging.api.myapp.com
DEBUG=false

# .env.prod
BASE_URL=https://api.myapp.com
DEBUG=false

Register in pubspec.yaml:

flutter:
  assets:
    - assets/env/.env
    - assets/env/.env.dev
    - assets/env/.env.staging
    - assets/env/.env.prod

3️⃣ Initialize #

In main.dart, before runApp():

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await EnvConfigService.instance.init(
    defaultEnv: Env.dev,
    allowProdSwitch: false,    // ⚠️ Lock prod by default
    verifyIntegrity: true,     // 🔐 Detect tampering
    onBeforeSwitch: (from, to) {
      debugPrint('Switching: ${from.name} → ${to.name}');
    },
  );

  runApp(const MyApp());
}

Wrap your app with the overlay:

MaterialApp(
  builder: (context, child) => EnvifiedOverlay(
    service: EnvConfigService.instance,
    enabled: kDebugMode,                        // 🚫 Hidden in production
    gate: EnvGate(pin: '1234'), // 🔐 PIN (Don't hardcode in a real app)
    trigger: const EnvTrigger.tap(count: 7),    // 7-tap to open
    child: child ?? const SizedBox.shrink(),
  ),
  home: const MyApp(),
)

Done. 7 taps anywhere on the screen and you can switch environments in real-time.


Core Usage Patterns #

Reading Values #

final svc = EnvConfigService.instance;

// String
final name = svc.get('APP_NAME');

// Typed (with fallbacks)
final timeout  = svc.getInt('TIMEOUT', fallback: 30);
final debug    = svc.getBool('DEBUG');
final apiUrl   = svc.getUri('BASE_URL');
final hosts    = svc.getList('ALLOWED_HOSTS'); // comma-separated

Reacting to Switches #

EnvConfigService.current is a ValueNotifier. Use it with:

ValueListenableBuilder:

ValueListenableBuilder<EnvConfig>(
  valueListenable: EnvConfigService.instance.current,
  builder: (context, config, _) {
    return Text('Env: ${config.env.name} (${config.baseUrl})');
  },
)

Listener (one-time setup):

EnvConfigService.instance.current.addListener(() {
  final config = EnvConfigService.instance.current.value;
  dio.options.baseUrl = config.baseUrl;
  analytics.setProperty('env', config.env.name);
});

Customizing the Panel #

EnvifiedOverlay(
  service: EnvConfigService.instance,
  trigger: EnvTrigger.shake(),                    // Shake to open
  gate: EnvGate(pin: '0000'),                     // PIN only (fetch securely in prod)
  showFab: false,                                 // Stealth mode (hidden button)
  child: child!,
)

Adding the Status Badge #

Display a persistent env indicator (optional):

Stack(
  children: [
    MyApp(),
    if (kDebugMode)
      EnvStatusBadge(
        service: EnvConfigService.instance,
        alignment: Alignment.topRight,            // Corner position
      ),
  ],
)

The badge pulses amber when a custom URL override is active.


Security & Production Safety #

🔒 Production Lock #

By default, allowProdSwitch: false locks the production environment:

await EnvConfigService.instance.init(
  allowProdSwitch: false,  // ← Once in prod, can't switch out
);

This prevents accidental data disasters. To unlock (dev only):

allowProdSwitch: true  // Use only in debug/test builds

🔐 Access Gate (PIN) #

Require authentication before opening the debug panel:

EnvGate(pin: '1234')                          // PIN only (Don't hardcode in real apps)

The gate auto-clears when the app is backgrounded. Next open requires re-auth.

✅ Tamper Detection #

SHA-256 integrity checks on .env* files:

await EnvConfigService.instance.init(
  verifyIntegrity: true,  // 🔍 Detect if .env was modified
);

Throws EnvifiedTamperException if a file changes after first load (rooted device attack detection).

📋 Audit Log #

Every switch and URL change is logged (encrypted, capped at 50 entries):

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 → prod)"
}

Last 10 entries visible in the debug panel.

⚙️ Zero Production Overhead #

All debug code is wrapped in kDebugMode:

EnvifiedOverlay(
  enabled: kDebugMode,  // ← Entire widget tree stripped in release
  ...
)

Tree-shaking removes the button, panel, and all gates from your release APK/IPA. Zero bytes added to production.


Gesture Triggers #

Choose how to open the panel:

Trigger Example Best for
Tap N times EnvTrigger.tap(count: 7) Universal (no special hardware)
Shake EnvTrigger.shake(threshold: 15.0) Mobile-friendly, intuitive
Edge swipe EnvTrigger.edgeSwipe(edgeWidth: 20) Stealth (easy to hide)

Stealth mode: Set showFab: false to hide the floating button and use only the gesture:

EnvifiedOverlay(
  trigger: EnvTrigger.shake(),
  showFab: false,  // 👻 Button completely hidden
  child: child!,
)

State Management Integration #

envified is framework-agnostic. Integrate with any state management:

GetX #

Get.put(EnvConfigService.instance, permanent: true);

// Later, anywhere:
final svc = Get.find<EnvConfigService>();
final baseUrl = svc.current.value.baseUrl;

Riverpod #

final envServiceProvider = Provider<EnvConfigService>((ref) {
  return EnvConfigService.instance;
});

final baseUrlProvider = Provider<String>((ref) {
  final svc = ref.watch(envServiceProvider);
  return svc.current.value.baseUrl;
});

BLoC #

EnvConfigService.instance.current.addListener(() {
  add(EnvChanged(EnvConfigService.instance.current.value));
});

Lifecycle Hooks #

Run code before/after environment switches:

await EnvConfigService.instance.init(
  onBeforeSwitch: (from, to) async {
    // Flush pending HTTP requests
    await _api.flushQueue();
    // Wait for active transactions to complete
    await _db.waitForCommits();
  },
  onAfterSwitch: (config) {
    // Update HTTP client
    _dio.options.baseUrl = config.baseUrl;
    // Log to analytics
    _analytics.logEvent('env_switched', {'env': config.env.name});
    // Refresh UI
    _eventBus.emit(EnvChangedEvent(config));
  },
);

Migration from v1.0.0 #

All new features are optional with sensible defaults. Your existing v1.0.0 code works unchanged:

// v1.0.0 style — still works
await EnvConfigService.instance.init(defaultEnv: Env.dev);

EnvifiedOverlay(
  service: EnvConfigService.instance,
  enabled: kDebugMode,
  child: child!,
)

The only breaking change: EnvStorage.clear() now also wipes URL history and audit log (desired for full reset). If you need selective deletion, use the new targeted methods.


❓ FAQ #

Q: Does this slow down my app?
A: No. The service is lazy-initialized and UI is completely stripped in release builds via tree-shaking. Zero impact.

Q: What if a .env file is deleted?
A: The service throws EnvifiedMissingFileException on init. This is intentional — fail loudly, not silently.

Q: Can I switch prod at runtime?
A: Only if you set allowProdSwitch: true. Default is locked for safety. Recommended: unlock only in debug builds.

Q: What about secrets and API keys?
A: Don't put secrets in .env files. Use a secure backend. .env is for non-sensitive config only (URLs, timeouts, feature flags).

Q: Do users see the debug button in production?
A: No. All debug code is wrapped in if (kDebugMode) and stripped via tree-shaking in release builds.

Q: Can I customize colors and fonts?
A: Yes. Pass EnvifiedTheme to EnvifiedOverlay to override everything.

Q: Works with web?
A: Partially. Web doesn't support shake detection. Tap trigger and PIN gate work fine.


🔄 API Reference #

Full docs: pub.dev/documentation/envified

EnvConfigService (Singleton) #

// Initialization
await EnvConfigService.instance.init({
  defaultEnv,           // Env.dev (default)
  allowProdSwitch,      // false (default, locked)
  verifyIntegrity,      // false (default)
  onBeforeSwitch,       // Function?
  onAfterSwitch,        // Function?
});

// Reading values
final value = svc.get('KEY');
final bool = svc.getBool('DEBUG');
final int = svc.getInt('TIMEOUT', fallback: 30);
final uri = svc.getUri('BASE_URL');
final list = svc.getList('ALLOWED_HOSTS');

// Switching
await svc.switchTo(Env.prod);
await svc.setBaseUrl('https://custom.url');
await svc.clearBaseUrlOverride();

// Lifecycle
svc.current              // ValueNotifier<EnvConfig>
await svc.auditLog      // List<AuditEntry>

// Reset
await svc.reset();

Widgets #

// Overlay + Panel
EnvifiedOverlay(
  service: EnvConfigService.instance,
  enabled: kDebugMode,
  gate: EnvGate(...),
  trigger: EnvTrigger.tap(),
  showFab: true,
  child: child,
)

// Status indicator
EnvStatusBadge(
  service: EnvConfigService.instance,
  alignment: Alignment.topRight,
)

// Manual panel (no overlay)
EnvDebugPanel(
  service: EnvConfigService.instance,
)

Models #

enum Env { dev, staging, prod, custom }

class EnvConfig {
  final Env env;
  final String baseUrl;
  final Map<String, String> extras;
}

class EnvGate {
  EnvGate({String? pin});
}

sealed class EnvTrigger {
  factory EnvTrigger.tap({int count = 7}) = _TapTrigger;
  factory EnvTrigger.shake({double threshold = 15.0}) = _ShakeTrigger;
  factory EnvTrigger.edgeSwipe({double edgeWidth = 20}) = _EdgeSwipeTrigger;
}

class AuditEntry {
  final DateTime timestamp;
  final String action;        // 'switch', 'setBaseUrl', etc.
  final String? fromEnv;
  final String? toEnv;
  final String? url;
}

🤝 Contributing #

Found a bug? Have a feature idea? We'd love your help!

  1. Fork the repo
  2. Create a branchgit checkout -b feat/amazing-idea
  3. Make changes — add tests for new code
  4. Commitgit commit -m 'feat: add amazing idea'
  5. Pushgit push origin feat/amazing-idea
  6. Open a PR — and let's ship it together! 🚀

See CONTRIBUTING.md for details.


🐛 Issues & Feedback #


Support the Project ☕ #

envified is 100% open source and free. Built and maintained by Sumit Pal in spare time.

If it saves you hours of rebuild time, consider buying me a chai. Direct UPI — zero fees, 100% goes to me.

Amount What it means
₹20 You liked it
🍵 ₹50 Saved you time
🚀 ₹100 In production

📄 License #

MIT © Appamania

Built with ❤️ for Flutter developers who value time, security, and sanity.

0
likes
160
points
--
downloads

Documentation

API reference

Publisher

verified publisherappamania.in

Weekly Downloads

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.

Homepage
Repository (GitHub)
View/report issues

Topics

#environment #configuration #env #debugging #devtools

Funding

Consider supporting this project:

paywithchai.in

License

MIT (license)

Dependencies

crypto, flutter, flutter_secure_storage, sensors_plus

More

Packages that depend on envified