Encrypt Env

pub package package publisher

A Dart CLI tool that generates obfuscated or encrypted Dart files from YAML or JSON configuration. It helps you protect API keys, secrets, tokens, and other sensitive data in Flutter and Dart applications.

Summary

Installation

Activate globally via Dart:

dart pub global activate encrypt_env

Quick start

Run without arguments for an interactive experience:

encrypt_env gen

The CLI will guide you through each option:

? Choose a mode:
❯ XOR obfuscation (no dependencies)
  AES-256 encryption (requires fortis)

? Choose a case style:
❯ camelCase
  snake_case
  SCREAMING_SNAKE_CASE

? Folder containing your config files: (environment)
? Config file name (without extension): (environment)
? Environment to merge (e.g. dev, prod — leave empty to skip):
? Output directory for generated Dart file: (lib)
? Output file name (without .dart): (environment)
? Generate test file? (Y/n)

Or pass flags directly for automation:

encrypt_env gen --style cc --env prod

When any flag is passed, the CLI uses default values for the remaining options without prompting.

Modes

XOR obfuscation

The default mode. Uses multi-layer XOR obfuscation with per-value salt, byte shuffling, and derived key passes. The generated file has zero external dependencies.

encrypt_env gen

Values are obfuscated — not visible as plain text in the source code or binary, but not cryptographically secure. Ideal for base URLs, SDK keys, and feature flags.

Flutter projects: For additional protection, enable Flutter's built-in obfuscation when building for release:

flutter build apk --obfuscate --split-debug-info=debug-info/

AES-256 encryption

Uses AES-256-GCM encryption via the fortis package. The generated file requires fortis as a dependency and a runtime key to decrypt.

# With your own key
encrypt_env gen --encrypt --key <base64_key>

# Auto-generate a key
encrypt_env gen --encrypt

When no key is provided, the CLI generates one and displays it in the console:

Generated a new AES-256 key:

hEvB+s5mpCLmmioI6Ji53JwIzx7ZQ5HUih/7CTv5S2I=

⚠ Save this key securely. You will need it at runtime.

The generated file includes an EncryptEnv class with an init() method that must be called before accessing any value:

import 'environment.dart';

void main() {
  EncryptEnv.init('your-base64-key-here');
  print(Environment.baseUrl);
}

Setup

Organize your project with a folder named environment and a config file (.yaml, .yml, or .json):

your_project/
├── environment/
│   └── environment.yaml   # or .yml or .json

The CLI auto-detects the file format. Priority order: .yaml > .yml > .json.

You can change the folder and file name using --folder and --config flags.

Basic example

Given the following environment/environment.yaml:

environment:
  base_url: 'http://localhost:3000'
  version: '1.0.0'
  production: false
  headers:
    api-key: 'value'
endpoint:
  endpoint_a: 'endpoint-a'
  endpoint_b: 'endpoint-b'

Run:

encrypt_env gen

The file lib/environment.dart will be generated with sealed classes and strongly-typed getters:

sealed class Environment {
  static String get baseUrl {
    final List<int> encoded = [0xd5, 0x98, ...];
    final List<int> salt = [...[0xaa, 0xbb, ...], ...[0xcc, 0xdd, ...]];

    return _decode(encoded, salt);
  }

  static bool get production {
    final List<int> encoded = [0xdb, 0x8d, ...];
    final List<int> salt = [...[0x94, 0xb5, ...], ...[0xe3, 0xa0, ...]];

    return bool.parse(_decode(encoded, salt));
  }

  static Map<String, dynamic> get headers {
    return {
      _decode([0xdc, ...], [...[...], ...[...]]): _apiKey,
    };
  }
}

sealed class Endpoint {
  static String get endpointA { ... }
  static String get endpointB { ... }
}

Each value has its own unique salt, split into two fragments for additional obscurity.

Merging environments

You can merge environment-specific overrides on top of a base config:

# environment/environment.yaml (base)
environment:
  production: false
  base_url: 'http://localhost:3000'
  api_key: 'dev_key'
# environment/prod_environment.yaml (overrides)
environment:
  production: true
  base_url: 'https://api.example.com'
  api_key: 'prod_key'
encrypt_env gen -e prod

Values from prod_environment.yaml override the base config. Unspecified values are preserved from the base.

Use any prefix: staging, dev, uat, etc. Format: {prefix}_environment.yaml.

Key generation

Generate a random AES-256 key:

encrypt_env keygen
AES-256 key generated:

y67ImXMjCr1Uuo6jvF0pXBuomlshiwCgbwYQFRiUHbk=

Test generation

By default, a test file is generated alongside the Dart file at test/{out-file}_test.dart. The CLI automatically detects your project type by reading pubspec.yaml:

  • Flutter project (has flutter dependency) → uses package:flutter_test/flutter_test.dart
  • Dart project → uses package:test/test.dart
  • Package name → uses package:name/... import style

Example generated test:

import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/environment.dart';

void main() {
  group('Environment', () {
    test('baseUrl returns correct value', () {
      expect(Environment.baseUrl, 'http://localhost:3000');
    });

    test('production returns correct value', () {
      expect(Environment.production, false);
    });

    test('production returns bool', () {
      expect(Environment.production, isA<bool>());
    });
  });
}

To disable test generation:

encrypt_env gen --no-test

⚠ In --encrypt mode, the generated test embeds the AES-256 key in plaintext. Do not commit it to a public repository — add the test file to .gitignore or rotate the key before publishing.

Customization

Available flags

Flag Default Description
--folder environment Folder containing your configuration files
--config environment Base config file name (without extension)
-e, --env none Environment name to merge (e.g., dev, prod)
--out-dir lib Output directory for the generated Dart file
--out-file environment Output Dart file name (without .dart)
-s, --style cc Getter naming style: cc (camelCase), sc (snake_case), ssc (SCREAMING_SNAKE_CASE)
--encrypt false Use AES-256-GCM encryption instead of XOR obfuscation
-k, --key none Base64 AES-256 key (used with --encrypt)
--[no-]test true Generate a test file alongside the Dart file

Documentation

Detailed documentation about how each mode works:

Help

To view all available commands and options:

encrypt_env -h

License

MIT License - see LICENSE for details.

Libraries

encrypt_env
Generate obfuscated or encrypted Dart files from YAML/JSON configuration.