Vault Storage
A secure, fast, and simple local storage solution for Flutter. Built on Hive and Flutter Secure Storage with AES-GCM encryption. Provides key-value storage and encrypted file storage with full web compatibility. Heavy crypto/JSON/base64 work runs in background isolates to keep your UI smooth.
NEW in v2.2.0: FreeRASP integration brings optional jailbreak protection and runtime security monitoring!
Migration Benefits
Switching from the "package dance" to Vault Storage provides immediate benefits:
Before (Multiple Packages)
// Managing 4+ different packages with different APIs
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';
// Complex initialization
await Hive.initFlutter();
final normalBox = await Hive.openBox('normal');
final secureStorage = FlutterSecureStorage();
final prefs = await SharedPreferences.getInstance();
// Different APIs for each storage type
prefs.setString('theme', 'dark'); // SharedPreferences API
normalBox.put('cache', data); // Hive API
await secureStorage.write(key: 'token', value: jwt); // Secure Storage API
// Manual file handling with encryption
final dir = await getApplicationDocumentsDirectory();
final file = File('${dir.path}/encrypted_file.dat');
// ...custom encryption logic...
After (Vault Storage)
import 'package:vault_storage/vault_storage.dart';
// Single initialization
final storage = VaultStorage.create();
await storage.init();
// Unified API for all storage needs
await storage.saveNormal(key: 'theme', value: 'dark');
await storage.saveNormal(key: 'cache', value: data);
await storage.saveSecure(key: 'token', value: jwt);
await storage.saveSecureFile(
key: 'document',
fileBytes: bytes,
originalFileName: 'file.pdf',
);
Results
- 90% less boilerplate code
- Single API to learn and maintain
- Built-in encryption - no manual crypto implementation
- Consistent error handling across all storage types
- Web compatibility without additional logic jailbreak protection and runtime security monitoring with FreeRASP integration!
Table of Contents
- Features
- Use Cases
- Why Choose Vault Storage?
- Security Features (NEW!)
- Important Notes for Production Use
- Compliance & Standards
- Getting Started
- Migration Guide: 1.x -> 2.0
- Quick Start
- Platform Setup
- Usage
- Performance Tuning
- Error Handling
- Troubleshooting
- Platform Support
Features
- Simple API: Intuitive methods with clear intent (saveSecure/saveNormal/get/delete/clear...)
- Smart lookups: get() checks normal first, then secure for performance, or constrain via isSecure
- List stored keys: keys() returns existing keys (optionally filter secure/normal and include file keys)
- Encrypted file storage: Secure (AES-GCM 256-bit) and normal file storage, unified API across platforms
- Jailbreak protection: Optional integration with FreeRASP for runtime security monitoring
- Threat detection: Detect rooting, debugging, app tampering, and more - fully customizable
- Web compatible: Native file system on devices; web stores bytes in Hive and auto-downloads on retrieval
- Fast by default: Crypto, JSON (large), and base64 (large) are offloaded to isolates
- Large-file streaming: Secure file encryption supports chunked streaming to reduce memory pressure
- Configurable performance: Tweak isolate thresholds via VaultStorageConfig
- Framework agnostic: Works with any state management or none
Use Cases
Built for apps that actually matter. Whether you're building the next unicorn or just want your users' data to be safe, Vault Storage has you covered:
Healthcare & Medical Apps
- Patient Records: Keep medical data secure and HIPAA-compliant out of the box
- Medical Imaging: Store diagnostic images offline without compromising security
- Health Tracking: Protect sensitive health data with enterprise-grade encryption
Financial & Banking Apps
- Auth Tokens: Store JWT tokens and API keys the right way
- Transaction Data: Keep financial records encrypted and PCI DSS compliant
- Biometric Data: Secure fingerprints and face ID templates safely
- Digital Wallets: Protect cryptocurrency keys and payment info
Enterprise & Business Apps
- Corporate Docs: Encrypt contracts and confidential documents
- Employee Data: Manage credentials and personal info securely
- API Keys: Store third-party service credentials safely
- Audit Trails: Maintain encrypted logs for compliance
- Tamper Protection: Detect if apps are modified or running in compromised environments
Consumer Apps with Real Users
- Password Managers: Store encrypted passwords and secure notes
- Messaging Apps: Cache encrypted messages and media files
- Personal Vaults: Secure storage for IDs, certificates, and important docs
- Social Apps: Protect user data and private content
- Device Security: Block access on jailbroken/rooted devices for enhanced protection
Cross-Platform Apps
- Consistent Security: Same protection across mobile, web, and desktop
- Large Files: Handle encrypted images, videos, and documents efficiently
- Offline-First: Secure local storage that works without internet
Why Choose Vault Storage?
Modern apps demand modern security. Even simple applications benefit from robust, future-proof storage.
Built for Real-World Applications
- Security by Default: Why worry about data breaches? Get enterprise-grade encryption out of the box
- Runtime Protection: Optional jailbreak detection and app integrity monitoring
- Performance First: Smooth UI even with heavy encryption - operations run in background isolates
- True Cross-Platform: One API that works consistently across mobile, web, and desktop
- Clear Error Handling: Explicit exceptions with typed StorageError subtypes
Future-Proof Your App
- Scalable: Start with simple key-value storage, seamlessly add encrypted file storage as you grow
- Compliance Ready: Already meet GDPR, HIPAA, and PCI DSS requirements without extra work
- Production Ready: Used in real-world applications handling sensitive user data
- Pragmatic: Simple, readable API with robust internals
Developer Experience
- Clean API: Simple, intuitive methods that handle complex security behind the scenes
- Batteries Included: Error types, utilities, and comprehensive documentation
- Well Tested: 97.5% test coverage gives you confidence in reliability
- Complete Documentation: Examples, use cases, and troubleshooting guides
Perfect for Both Simple and Complex Apps
Growing App? Start storing user preferences securely, then add document encryption later - same API.
Enterprise App? Get the security and compliance features you need without the complexity.
Consumer App? Your users' data deserves protection, and they'll notice the smooth performance.
Pro Tip: Even if you're storing "just preferences" today, using proper security from day one prevents costly migrations later when you add user accounts, premium features, or sensitive data.
Security Features (NEW!)
Vault Storage now includes optional runtime security monitoring powered by FreeRASP to protect your app and user data from advanced threats.
Platform Support: Security features are only available on Android and iOS. On other platforms (macOS, Windows, Linux, Web), the security configuration will be safely ignored and vault storage will work normally without security monitoring.
What's Protected
- Jailbreak/Root Detection: Block access on compromised devices
- App Tampering: Detect if your app has been modified or repackaged
- Debug Detection: Prevent debugging in production builds
- Hook Detection: Detect runtime manipulation frameworks (Frida, Xposed, etc.)
- Emulator Detection: Identify when running on emulators/simulators
- Unofficial Store: Detect installation from unofficial app stores
- Screen Capture: Monitor screenshots and screen recording
- System VPN: Detect system-level VPN usage
- Device Security: Check for device passcode and secure hardware
Quick Start with Security
import 'package:vault_storage/vault_storage.dart';
// Create storage with security enabled (Android/iOS only)
final storage = VaultStorage.create(
securityConfig: VaultSecurityConfig.production(
watcherMail: 'security@mycompany.com',
threatCallbacks: {
SecurityThreat.jailbreak: () {
// Custom handling - log event, show warning, etc.
print('Warning: Jailbreak detected - limiting functionality');
},
SecurityThreat.tampering: () {
// Handle app tampering - could exit app
print('Alert: App integrity compromised');
exit(0);
},
},
),
);
// Initialize with platform-specific security config
// Note: Security features only work on Android and iOS
await storage.init(
packageName: 'com.mycompany.myapp', // Android
signingCertHashes: ['your_cert_hash'], // Android signing cert
bundleId: 'com.mycompany.myapp', // iOS
teamId: 'YOUR_TEAM_ID', // iOS team ID
);
// Use normally - security runs automatically in background
await storage.saveSecure(key: 'api_key', value: 'secret');
Security Configuration Options
Development Mode
Perfect for testing and development:
final devConfig = VaultSecurityConfig.development(
watcherMail: 'dev@mycompany.com',
threatCallbacks: {
SecurityThreat.jailbreak: () => print('Jailbreak detected in dev'),
},
);
// - Blocks nothing by default
// - Logs all threats
// - Allows debugging and emulators
// - Only works on Android and iOS
Production Mode
Maximum security for production apps:
final prodConfig = VaultSecurityConfig.production(
watcherMail: 'security@mycompany.com',
threatCallbacks: {
SecurityThreat.jailbreak: () => showSecurityWarning(),
SecurityThreat.tampering: () => exit(0),
},
);
// - Blocks jailbreak, tampering, unofficial stores
// - Minimal logging
// - Strict security posture
// - Only works on Android and iOS
Custom Configuration
Full control over security behavior:
final customConfig = VaultSecurityConfig(
enableRaspProtection: true,
isProd: true,
watcherMail: 'security@mycompany.com',
// Granular control over blocking
blockOnJailbreak: true,
blockOnTampering: true,
blockOnHooks: true,
blockOnDebug: false, // Allow debugging
blockOnEmulator: false, // Allow emulators for testing
blockOnUnofficialStore: true,
enableLogging: true,
threatCallbacks: {
SecurityThreat.jailbreak: () => handleJailbreak(),
SecurityThreat.tampering: () => handleTampering(),
SecurityThreat.screenshot: () => logScreenCapture(),
// ... handle other threats as needed
},
);
// Note: Only effective on Android and iOS platforms
Security Error Handling
When security threats are detected and blocking is enabled:
try {
await storage.saveSecure(key: 'sensitive_data', value: data);
} on JailbreakDetectedException {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Security Warning'),
content: Text('This device appears to be jailbroken. Some features may be limited.'),
),
);
} on TamperingDetectedException {
// App has been modified - consider exiting
exit(0);
} on SecurityThreatException catch (e) {
print('Security threat detected: ${e.threatType} - ${e.message}');
}
Getting Your App Certificates
For Android apps, you need your signing certificate hash in Base64 format:
# For debug builds
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
# For release builds
keytool -list -v -keystore your-release-key.keystore -alias your-key-alias
# Look for SHA256 fingerprint, convert to Base64
For iOS, you need your Team ID from Apple Developer Console.
Security Monitoring Dashboard
FreeRASP provides a free monitoring dashboard where you can:
- View security events from your apps
- Compare threat levels to global averages
- Track security trends over time
- Export security reports
Register at https://freerasp.talsec.app/ using the same email as watcherMail
.
Without Security Features
Security features are completely optional. If you don't provide a securityConfig
, Vault Storage works exactly as before:
// No security features - works on all platforms
final storage = VaultStorage.create();
await storage.init();
Cross-Platform Compatibility: When you enable security features, your app will work seamlessly across all platforms:
- Android & iOS: Full security monitoring and threat detection
- macOS, Windows, Linux, Web: Normal vault storage without security features
This allows you to deploy the same codebase across all platforms without platform-specific conditional logic.
Important Notes for Production Use
Security Disclaimer: While Vault Storage implements industry-standard encryption (AES-GCM 256-bit) and follows security best practices, no software can guarantee 100% security. Always conduct your own security audits and compliance reviews before using in production applications, especially those handling sensitive data.
Security Considerations
- Audit Required: Perform independent security audits for applications handling sensitive data
- Compliance: Verify that your implementation meets your specific regulatory requirements
- Key Management: The security of your data depends on the platform's secure storage implementation
- Testing: Thoroughly test encryption/decryption flows in your specific use case
- RASP Limitations: While FreeRASP provides excellent runtime protection, no security solution is 100% foolproof
- False Positives: Security features may occasionally trigger false positives - test thoroughly in your environment
Legal & Compliance
- Your Responsibility: You are responsible for ensuring compliance with applicable laws and regulations
- Data Protection: Review data protection requirements for your jurisdiction and industry
- User Consent: Ensure proper user consent for data collection and storage
- Backup Strategy: Implement appropriate backup and recovery procedures
Best Practices
- Regular Updates: Keep the package and dependencies updated for security patches
- Error Handling: Implement comprehensive error handling for storage failures
- Data Minimisation: Only store data that you actually need
- Access Control: Implement proper access controls in your application layer
- Security Testing: Test your app's behavior when security threats are detected
- Graceful Degradation: Design your app to handle security blocks gracefully
Recommendation: For mission-critical applications, consider additional security measures such as certificate pinning, runtime application self-protection (RASP), and regular penetration testing.
Compliance & Standards
Vault Storage is designed to help meet common regulatory requirements:
Healthcare (HIPAA)
- AES-256-GCM encryption for all sensitive data
- Platform keychain storage for encryption keys
- Secure file handling for medical documents and images
- Access logging capabilities for audit trails
- Data minimization through selective encryption
Financial (PCI DSS)
- Strong cryptography for payment data protection
- Secure key management using platform secure storage
- File encryption for financial documents
- Runtime security monitoring (Android/iOS) to detect tampering
Enterprise (SOC 2)
- Data encryption at rest for sensitive corporate data
- Access controls through app-level implementation
- Security monitoring with FreeRASP integration
- Error logging for security event tracking
GDPR Compliance
- Data encryption to protect personal information
- Right to deletion through clear storage management
- Data portability through standardized export/import
- Minimal data collection - no analytics or telemetry
Important: While Vault Storage provides security foundations, you are responsible for ensuring your complete application meets regulatory requirements. Conduct security audits and compliance reviews before production deployment.
Getting Started
Add vault_storage
to your pubspec.yaml
file:
dependencies:
# ... other dependencies
vault_storage: ^2.0.0 # Replace with the latest version
Then, run:
flutter pub get
Migration Guide: 1.x -> 2.0
This release simplifies the API and removes the BoxType
-driven/Either-based surface. Key changes and how to migrate:
Why this change?
- Clarity and intent: Methods like
saveSecure
,saveNormal
, andget(..., isSecure)
are explicit and reduce ambiguity and misuse - Simpler error handling: Throwing typed
StorageError
exceptions simplifies flows compared toEither
-based handling sprinkled across call sites - Less leakage of internals: Removing
BoxType
prevents coupling callers to storage implementation details - Web and files ergonomics: A single key-based file API (with auto-download on web) is easier to use than passing back metadata maps
- Performance and maintainability: A smaller, clearer surface makes it easier to optimize internals (isolates, streaming) and evolve features safely
Trade-offs (considered acceptable):
- Exceptions require
try/catch
instead of.fold()
patterns - Web downloads now use a sensible default filename rather than app-controlled names in this simplified API
- Initialisation and errors
- Before:
await storage.init()
returnedEither
, handled via.fold()
- After:
await storage.init()
throws on failure. Use try/catch.
- Key-value API
- Before:
await storage.set(BoxType.secure, 'k', 'v')
final v = await storage.get<String>(BoxType.secure, 'k')
- After:
await storage.saveSecure(key: 'k', value: 'v')
final v = await storage.get<String>('k', isSecure: true)
- Normal data:
saveNormal(...)
andget(..., isSecure: false)
- Delete:
await storage.delete('k')
(removes from both storages) - Clear:
await storage.clearNormal()
,await storage.clearSecure()
- File storage
- Before:
saveSecureFile(fileBytes, fileExtension)
returned metadata Map you stored and later passed togetSecureFile(metadata, downloadFileName: ...)
- After:
await saveSecureFile(key: 'profile', fileBytes: bytes, originalFileName: 'x.jpg', metadata: {...})
- Retrieve with
await getFile('profile')
(auto-detect secure/normal); optionally constrain viaisSecure
- Delete with
await deleteFile('profile')
- Web: files auto-download on
getFile()
; custom filenames are not configurable in this API
- Error handling
- Before:
Either<StorageError, T>
results - After: methods throw
StorageError
subclasses (StorageReadError
,StorageWriteError
, etc.)
- Performance & internals
- Large JSON/base64 handled via isolates; thresholds configurable in
VaultStorageConfig
- Secure file encryption supports streaming for large files
- More aggressive Hive compaction strategy internally
See CHANGELOG for full details.
Migration Benefits
Switching from the "package dance" to Vault Storage provides immediate benefits:
Before (Multiple Packages)
// Managing 4+ different packages with different APIs
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';
// Complex initialization
await Hive.initFlutter();
final normalBox = await Hive.openBox('normal');
final secureStorage = FlutterSecureStorage();
final prefs = await SharedPreferences.getInstance();
// Different APIs for each storage type
prefs.setString('theme', 'dark'); // SharedPreferences API
normalBox.put('cache', data); // Hive API
await secureStorage.write(key: 'token', value: jwt); // Secure Storage API
// Manual file handling with encryption
final dir = await getApplicationDocumentsDirectory();
final file = File('${dir.path}/encrypted_file.dat');
// ...custom encryption logic...
After (Vault Storage)
import 'package:vault_storage/vault_storage.dart';
// Single initialization
final storage = VaultStorage.create();
await storage.init();
// Unified API for all storage needs
await storage.saveNormal(key: 'theme', value: 'dark');
await storage.saveNormal(key: 'cache', value: data);
await storage.saveSecure(key: 'token', value: jwt);
await storage.saveSecureFile(
key: 'document',
fileBytes: bytes,
originalFileName: 'file.pdf',
);
Results
- 90% less boilerplate code
- Single API to learn and maintain
- Built-in encryption - no manual crypto implementation
- Consistent error handling across all storage types
- Web compatibility without additional logic
Quick Start
Use the factory to create an instance and initialise once at app start:
import 'package:vault_storage/vault_storage.dart';
final storage = VaultStorage.create();
await storage.init();
// Save values
await storage.saveSecure(key: 'api_key', value: 'my_secret_key');
await storage.saveNormal(key: 'theme', value: 'dark');
// Read values (normal first, then secure)
final token = await storage.get<String>('api_key');
final theme = await storage.get<String>('theme');
// Constrain lookup to secure or normal storage
final secureOnly = await storage.get<String>('api_key', isSecure: true);
final normalOnly = await storage.get<String>('theme', isSecure: false);
The factory returns IVaultStorage
and hides implementation details.
Platform Setup
This package uses flutter_secure_storage
for secure key management, which requires platform-specific configurations:
Android
In android/app/build.gradle
, set minimum SDK version to 23 or higher for security features (18+ for basic storage):
android {
defaultConfig {
minSdkVersion 23 // Required for FreeRASP security features
// minSdkVersion 18 // Minimum for basic storage without security
}
}
Security Features (FreeRASP) - Additional Setup
For apps using security features, add these permissions to android/src/main/AndroidManifest.xml
:
<!-- For screenshot and screen recording detection (optional) -->
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
<uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />
Update Gradle and Kotlin versions in android/settings.gradle
:
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.8.1" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}
Note: To prevent backup-related keystore issues, consider disabling auto-backup in your AndroidManifest.xml
:
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:dataExtractionRules="@xml/data_extraction_rules"
...>
iOS
No additional configuration required for basic storage. The package uses iOS Keychain by default.
Security Features (FreeRASP) - Additional Setup
For apps using security features, ensure Xcode 15+ is installed for building.
macOS
Required: Add Keychain access entitlements and configure codesigning to prevent StorageInitializationError
.
1. Create or update entitlement files
macos/Runner/DebugProfile.entitlements
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
</array>
</dict>
</plist>
macos/Runner/Release.entitlements
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
</array>
</dict>
</plist>
2. Configure Codesigning
Ensure proper codesigning is configured in your Xcode project:
- Open
macos/Runner.xcworkspace
in Xcode - Select the Runner project in the navigator
- Go to "Signing & Capabilities" tab
- Ensure:
- Team is selected (required for keychain access)
- Bundle Identifier matches your app's identifier
- Signing Certificate is valid
- Keychain Sharing capability is added (should appear automatically with the entitlements)
3. Clean and rebuild after setup
flutter clean
flutter pub get
flutter run -d macos
4. Troubleshooting macOS Issues
If you still encounter StorageInitializationError
:
- Check Console app for detailed error messages from your app
- Verify codesigning: Run
codesign -dv --verbose=4 /path/to/your/app.app
to verify signatures - Reset Keychain (development only): Delete keychain entries for your app if corrupted
- Check entitlements: Run
codesign -d --entitlements - /path/to/your/app.app
to verify entitlements are applied
Why this is required: VaultStorage creates secure encryption keys using macOS Keychain during initialization. Without proper entitlements and codesigning, the system denies access to the Keychain, causing the StorageInitializationError.
Linux
Install required system dependencies:
sudo apt-get install libsecret-1-dev libjsoncpp-dev
For runtime: libsecret-1-0
and libjsoncpp1
Windows
No additional configuration required. Note: readAll
and deleteAll
operations have limitations on Windows.
Web
The package uses WebCrypto on web. Important notes:
- Use HTTPS in production; consider adding Strict-Transport-Security headers
- Data is browser/domain specific and non-portable
- Auto-downloads occur when retrieving files
Recommended header:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Before running your app, you must initialise the service. This is typically done in your main.dart
:
import 'package:flutter/material.dart';
import 'package:vault_storage/vault_storage.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialise the vault storage
final storage = VaultStorage.create();
try {
await storage.init();
runApp(MyApp(storage: storage));
} on StorageError catch (e) {
// Handle initialisation error appropriately
debugPrint('Failed to initialise storage: ${e.message}');
runApp(const ErrorApp());
}
}
class MyApp extends StatelessWidget {
final IVaultStorage storage;
const MyApp({super.key, required this.storage});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: MyHomePage(storage: storage),
);
}
}
// List keys (unique, sorted)
final allKeys = await storage.keys(); // both normal and secure; includes file keys
Usage
Framework-Agnostic Design
Important: Vault Storage doesn't depend on any state management framework. You can use it with:
- No framework: Direct instantiation and usage
- Riverpod: Optional integration examples provided
- Bloc: Use with any Bloc pattern
- Provider: Compatible with Provider pattern
- GetX: Works with GetX state management
- Any other solution: The package adapts to your architecture
The core package has zero dependencies on state management frameworks.
Basic Usage (No Dependencies)
Use Vault Storage directly without any state management framework:
import 'package:vault_storage/vault_storage.dart';
class StorageManager {
static IVaultStorage? _instance;
static Future<IVaultStorage> get instance async {
if (_instance != null) return _instance!;
final s = VaultStorage.create();
await s.init();
return _instance = s;
}
}
// Usage example
Future<void> example() async {
final storage = await StorageManager.instance;
await storage.saveSecure(key: 'api_key', value: 'my_secret_key');
final value = await storage.get<String>('api_key', isSecure: true);
debugPrint('Retrieved API key: $value');
}
Using with Riverpod
Create your own provider if you use Riverpod:
dependencies:
vault_storage: ^2.0.0
flutter_riverpod: ^2.5.0
riverpod_annotation: ^2.3.3
dev_dependencies:
build_runner: ^2.4.7
riverpod_generator: ^2.3.9
Then create your own provider file: lib/providers/storage_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:vault_storage/vault_storage.dart';
part 'storage_provider.g.dart';
@Riverpod(keepAlive: true)
Future<IVaultStorage> vaultStorage(VaultStorageRef ref) async {
final implementation = VaultStorage.create();
await implementation.init();
ref.onDispose(() async => implementation.dispose());
return implementation;
}
Don't forget to run code generation:
dart run build_runner build
Then use it in your widgets:
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'providers/storage_provider.dart';
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return ref.watch(vaultStorageProvider).when(
data: (storage) => YourWidget(storage: storage),
loading: () => CircularProgressIndicator(),
error: (error, stack) => Text('Error: $error'),
);
}
}
And update your main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'providers/storage_provider.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final container = ProviderContainer();
try {
await container.read(vaultStorageProvider.future);
runApp(
UncontrolledProviderScope(
container: container,
child: const MyApp(),
),
);
} catch (error) {
runApp(const ErrorApp());
}
}
Key-Value Storage
Store and retrieve simple key-value pairs:
// Secure data (encrypted)
await storage.saveSecure(key: 'user_token', value: 'jwt_token_here');
await storage.saveSecure(key: 'user_credentials', value: {
'username': 'john_doe',
'password': 'hashed_password',
});
// Normal data (faster, unencrypted)
await storage.saveNormal(key: 'theme_mode', value: 'dark');
await storage.saveNormal(key: 'language', value: 'en');
// Retrieve data
final token = await storage.get<String>('user_token', isSecure: true);
final theme = await storage.get<String>('theme_mode', isSecure: false);
File Storage (Secure and Normal)
For images, documents, or any binary data:
import 'dart:typed_data';
Future<void> handleFileStorage(IVaultStorage storage) async {
final Uint8List imageData = /* from picker/network */ Uint8List(0);
// Save a secure file (encrypted)
await storage.saveSecureFile(
key: 'profile_image',
fileBytes: imageData,
originalFileName: 'avatar.jpg',
metadata: {'userId': '123'}, // optional user metadata
);
// Save a normal file (unencrypted)
await storage.saveNormalFile(
key: 'cached_document',
fileBytes: imageData,
originalFileName: 'document.pdf',
);
// Retrieve file bytes by key
final secureBytes = await storage.getFile('profile_image'); // web auto-downloads
final normalBytes = await storage.getFile('cached_document', isSecure: false);
// Delete files
await storage.deleteFile('profile_image');
await storage.deleteFile('cached_document');
}
Note on web: When you call getFile(), the browser automatically downloads the file using a sensible default filename derived from the stored extension. Custom download filenames are not configurable in this simplified API.
Storage Classes Under the Hood
Internally, Vault Storage maintains separate boxes for normal/secure key-value data and normal/secure files. The public API abstracts this; you only provide keys and optional isSecure where relevant.
Platform-Specific Behaviour
The package automatically handles platform differences to provide the best user experience:
File Retrieval Behaviour
- Web: Auto-downloads file and returns Uint8List
- Native: Returns Uint8List only (no download)
File Storage Implementation
- Native platforms (iOS, Android, macOS, Windows, Linux): Files are stored in the app's documents directory using the file system
- Web: Files are stored as base64-encoded strings in encrypted Hive boxes (browser storage)
Automatic MIME Type Detection (Web)
For web downloads, MIME types are inferred from file extensions (PDF, images, common docs, audio/video, archives, JSON/TXT, etc.). If unknown, defaults to application/octet-stream
.
No code changes are required - the package handles platform detection and optimisation automatically.
Web Compatibility
- Native: Files are stored in the app's documents directory
- Web: Files are stored as base64-encoded strings in Hive (secure files encrypted), and auto-download when retrieved
Initialisation in main()
import 'package:flutter/material.dart';
import 'package:vault_storage/vault_storage.dart';
late final IVaultStorage vaultStorage;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialise storage
vaultStorage = VaultStorage.create();
await vaultStorage.init();
debugPrint('Storage initialised successfully');
runApp(const MyApp());
}
// Use the service anywhere in your app
Future<void> useStorage() async {
await vaultStorage.saveSecure(key: 'api_key', value: 'my_secret_key');
}
Error Handling
APIs throw typed exceptions that extend StorageError
. Use try/catch:
try {
await vaultStorage.saveSecure(key: 'k', value: 'v');
final v = await vaultStorage.get<String>('k', isSecure: true);
} on StorageInitializationError catch (e) {
debugPrint('Storage not initialised: ${e.message}');
} on StorageReadError catch (e) {
debugPrint('Read failed: ${e.message}');
} on StorageWriteError catch (e) {
debugPrint('Write failed: ${e.message}');
} on StorageSerializationError catch (e) {
debugPrint('Serialization failed: ${e.message}');
}
Storage Management
// Delete a key from both storages
await vaultStorage.delete('api_key');
// Clear storages
await vaultStorage.clearNormal();
await vaultStorage.clearSecure();
// Inspect keys
final keys = await vaultStorage.keys(includeFiles: true);
// Dispose (e.g., on shutdown)
await vaultStorage.dispose();
Performance Tuning
Vault Storage is optimized for performance by default, but you can fine-tune it for your specific workload:
// Optional: customize performance thresholds at startup
VaultStorageConfig.jsonIsolateThreshold = 15000; // chars
VaultStorageConfig.base64IsolateThreshold = 100000; // bytes
VaultStorageConfig.secureFileStreamingThresholdBytes = 1 * 1024 * 1024; // 1MB
Background Processing
Heavy operations are automatically moved to background isolates:
- JSON serialization/deserialization: For objects larger than the threshold
- Base64 encoding/decoding: For files larger than the threshold
- Encryption/decryption: All secure operations run in background
- File I/O: Large file operations are streamed to prevent memory issues
This keeps your UI thread responsive even when working with large data sets or files.
Benchmarks
Compared to manually managing multiple packages:
- Initialization: 2-3x faster setup with single
init()
call - Memory usage: 40% less memory overhead with unified storage
- Bundle size: Eliminates 3-4 separate dependencies
- Error handling: 80% reduction in error-handling code
Troubleshooting
Common Initialisation Errors
"Failed to create/decode secure key"
This error typically occurs when flutter_secure_storage
cannot access the platform's secure storage:
macOS: Ensure keychain access entitlements are properly configured (see Platform Setup above)
Android: Check that minSdkVersion >= 18 and consider disabling auto-backup
Solution: Verify platform-specific setup requirements and restart your app after configuration changes
App Crashes on First Launch
If the app crashes during storage initialisation:
- Check that all platform requirements are met
- Ensure proper error handling in your
main()
function:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final storage = VaultStorage.create();
final initResult = await storage.init();
initResult.fold(
(error) {
print('Storage initialisation failed: ${error.message}');
// Show error screen or fallback UI
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: Text('Storage initialisation failed: ${error.message}'),
),
),
));
},
(_) => runApp(MyApp(storage: storage)),
);
}
Web Storage Issues
For web applications:
- Ensure HTTPS is enabled for production
- Check browser compatibility with WebCrypto
- Verify that local storage is not disabled
Debug Mode
To get more detailed error information, check the console output when initialisation fails. The package provides detailed error messages for different failure scenarios.
Testing
To run tests:
flutter test
Dependencies
hive_ce_flutter
: Local storage databaseflutter_secure_storage
: Secure key storagecryptography_plus
: AES-GCM encryptionweb
: Modern web APIs for web downloadsfreerasp
: Optional runtime security monitoring and jailbreak protection (Android/iOS only)
Platform Support
- Android
- iOS
- Web
- macOS
- Windows
- Linux
License
This project is licensed under the MIT License.
Libraries
- vault_storage
- A barrel file that exports the public-facing API of the vault storage.