vault_storage 3.1.0
vault_storage: ^3.1.0 copied to clipboard
A package for secure key-value and file storage using Hive and flutter_secure_storage.
Vault Storage #
Secure, fast key-value and file storage for Flutter. Built on Hive and flutter_secure_storage with AES-GCM encryption, full web support, and background isolates for heavy crypto work.
NEW in v3.0.0: 20-50x faster List and Map operations via native storage. Automatic migration from v2.x, no breaking changes.
NEW in v2.3.0: Custom boxes for multi-tenant apps and data isolation.
NEW in v2.2.0: Optional FreeRASP integration — jailbreak detection and runtime security monitoring.
Migration Benefits #
Switching from the "package dance":
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 extra platform logic
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: Clear-intent methods (
saveSecure/saveNormal/get/delete/clear) - Smart lookups:
get()checks normal first, then secure; constrain viaisSecure - List stored keys:
keys()returns stored keys (filter by secure/normal, include file keys) - Encrypted file storage: AES-GCM 256-bit and normal file storage, unified API across platforms
- Jailbreak protection: Optional FreeRASP integration for runtime security
- Threat detection: Detect rooting, debugging, and app tampering
- Web compatible: Native file system on devices; web stores bytes in Hive with auto-download
- Fast by default: Crypto, JSON, and base64 offloaded to isolates for large payloads
- Large-file streaming: Chunked encryption reduces memory pressure for big files
- Configurable performance: Adjust isolate thresholds via
VaultStorageConfig - Framework agnostic: Works with any state management or none
Use Cases #
Healthcare & Medical Apps #
- Patient Records: Store medical data with HIPAA-grade encryption
- Medical Imaging: Save diagnostic images offline with encryption
- Health Tracking: Encrypt sensitive health data at rest
Financial & Banking Apps #
- Auth Tokens: Store JWT tokens and API keys securely
- Transaction Data: Encrypt financial records (PCI DSS compliant)
- Biometric Data: Protect fingerprint and face ID templates
- Digital Wallets: Secure cryptocurrency keys and payment info
Enterprise & Business Apps #
- Corporate Docs: Encrypt contracts and confidential documents
- Employee Data: Encrypt credentials and personal info
- API Keys: Encrypt third-party service credentials
- Audit Trails: Maintain encrypted logs for compliance
- Tamper Protection: Detect modified apps or 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: Encrypt IDs, certificates, and personal documents
- Social Apps: Protect user data and private content
- Device Security: Block access on jailbroken/rooted devices
Cross-Platform Apps #
- Consistent Security: Same protection across mobile, web, and desktop
- Large Files: Handle large encrypted files (images, videos, documents)
- Offline-First: Secure local storage that works without internet
Why Choose Vault Storage? #
One package replaces several, with encryption built in.
Built for Real-World Applications
- Security by Default: AES-256-GCM encryption for all secure data, no manual setup
- Runtime Protection: Optional jailbreak detection and app integrity monitoring
- Performance First: Crypto and JSON work runs in background isolates; the UI thread stays free
- True Cross-Platform: One API across mobile, web, and desktop
- Clear Error Handling: Typed
VaultStorageErrorsubtypes for every failure mode
Future-Proof Your App
- Scalable: Start with key-value storage, add encrypted files later — same API
- Compliance Ready: Covers GDPR, HIPAA, and PCI DSS data-at-rest requirements
- Production Ready: Used in production apps handling sensitive user data
- Pragmatic: Simple API, tested internals
Developer Experience
- Clean API: Methods that handle security internally
- Batteries Included: Error types, utilities, and full documentation
- Well Tested: 97.5% test coverage
- Complete Documentation: Examples, use cases, and troubleshooting guides
Perfect for Both Simple and Complex Apps
Growing app? Start with preferences, add file encryption later — same API.
Enterprise app? Security and compliance without the complexity.
Consumer app? Protect user data without compromising performance.
Pro Tip: Even if you store only preferences today, secure storage from day one prevents costly migrations when you add accounts, premium features, or sensitive data.
Security Features (NEW!) #
Optional runtime security monitoring via FreeRASP. Detects jailbreaks, tampering, hooking, and more on Android and iOS.
Platform Support: Security features run only on Android and iOS. Other platforms ignore the configuration and work normally.
What's Protected #
- Jailbreak/Root Detection: Block access on compromised devices
- App Tampering: Detect modified or repackaged apps
- Debug Detection: Prevent debugging in production builds
- Hook Detection: Detect runtime manipulation (Frida, Xposed, etc.)
- Emulator Detection: Detect emulators and simulators
- Unofficial Store: Detect installation from unofficial app stores
- Screen Capture: Monitor screenshots and screen recording
- System VPN: Detect system 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',
androidPackageName: 'com.mycompany.myapp',
androidSigningCertHashes: ['your_cert_hash'],
iosBundleId: 'com.mycompany.myapp',
iosTeamId: 'YOUR_TEAM_ID',
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 storage
// Note: Security features only work on Android and iOS
await storage.init();
// Use normally - security runs automatically in background
await storage.saveSecure(key: 'api_key', value: 'secret');
Security Configuration Options #
Development Mode
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
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
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 a blocked threat is detected:
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 #
Get your signing certificate hash (Base64):
# 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, get your Team ID from Apple Developer Console.
Security Monitoring Dashboard #
FreeRASP provides a free monitoring dashboard:
- 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 is optional. Without a securityConfig, storage works as before:
// No security features - works on all platforms
final storage = VaultStorage.create();
await storage.init();
Cross-Platform Compatibility: Security config is safe to include on all platforms:
- Android & iOS: Full security monitoring and threat detection
- macOS, Windows, Linux, Web: Normal vault storage without security features
One codebase, all platforms — no conditional logic needed.
Important Notes for Production Use #
Security Disclaimer: Vault Storage uses AES-GCM 256-bit encryption and follows security best practices, but no software guarantees absolute security. Conduct your own security audits before deploying to production with sensitive data.
Security Considerations #
- Audit Required: Audit independently before handling sensitive data
- Compliance: Verify your implementation meets your regulatory requirements
- Key Management: Secure storage quality varies by platform
- Testing: Test encryption/decryption in your specific use case
- RASP Limitations: FreeRASP adds runtime protection, but no security layer is infallible
- False Positives: Security features may trigger false positives — test in your environment
Legal & Compliance #
- Your Responsibility: Ensure compliance with applicable laws and regulations
- Data Protection: Review data protection requirements for your jurisdiction and industry
- User Consent: Obtain user consent for data collection and storage
- Backup Strategy: Plan for backup and recovery
Best Practices #
- Regular Updates: Keep the package and dependencies updated for security patches
- Error Handling: Handle storage failures explicitly
- Data Minimisation: Store only what you need
- Access Control: Add access controls at the application layer
- Security Testing: Test behavior when threats are detected
- Graceful Degradation: Handle security blocks gracefully
Recommendation: For mission-critical applications, add certificate pinning, RASP, and regular penetration testing.
Compliance & Standards #
Vault Storage helps 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: Vault Storage provides security foundations; you are responsible for full regulatory compliance. Audit before deploying to production.
Getting Started #
Add to your pubspec.yaml:
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 drops BoxType and Either, simplifying the API surface.
Why this change? #
- Clarity and intent:
saveSecure,saveNormal, andget(..., isSecure)name intent explicitly and reduce misuse - Simpler error handling: Typed
VaultStorageErrorexceptions replaceEither-based handling scattered across call sites - Less leakage of internals: Dropping
BoxTypedecouples callers from storage internals - Web and files ergonomics: A key-based file API (with web auto-download) replaces metadata maps
- Performance and maintainability: A smaller surface simplifies internal optimization and future changes
Trade-offs (considered acceptable):
- Exceptions require
try/catchinstead of.fold()patterns - Web downloads use a default filename rather than app-controlled names in this 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
VaultStorageErrorsubclasses (VaultStorageReadError,VaultStorageWriteError, etc.)
- Performance & internals
- Large JSON/base64 handled via isolates; thresholds configurable in
VaultStorageConfig - Large secure files use streaming encryption
- More aggressive Hive compaction
See CHANGELOG for full details.
Quick Start #
Use the factory to create an instance and initialise 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, hiding implementation details.
Custom Boxes (NEW in v2.3.0) #
Create isolated storage boxes for multi-tenancy or feature separation:
import 'package:vault_storage/vault_storage.dart';
// Create storage with custom boxes
final storage = VaultStorage.create(
customBoxes: [
BoxConfig(name: 'user_123', encrypted: true),
BoxConfig(name: 'workspace_abc', encrypted: true),
BoxConfig(name: 'cache', encrypted: false),
],
);
// Initialize storage
await storage.init();
// Use specific boxes
await storage.saveSecure(
key: 'api_key',
value: 'secret',
box: 'user_123', // Store in custom box
);
// Read from specific box
final value = await storage.get<String>(
'api_key',
box: 'user_123',
);
// Each box is completely isolated
await storage.saveNormal(
key: 'preferences',
value: {'theme': 'dark'},
box: 'workspace_abc',
);
Custom box uses:
- Multi-tenant apps: Separate storage per user or organization
- Feature isolation: Independent storage for different app modules
- Data lifecycle: Separate boxes for cache vs. persistent data
- Workspace management: Multiple workspaces with isolated data
Platform Setup #
This package uses flutter_secure_storage for key management, which needs platform-specific setup:
Android #
In android/app/build.gradle, set minimum SDK to 23+ 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
Add 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" />
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, disable auto-backup in AndroidManifest.xml:
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:dataExtractionRules="@xml/data_extraction_rules"
...>
iOS #
No additional setup needed for basic storage. The package uses iOS Keychain.
Security Features (FreeRASP) - Additional Setup
For apps using security features, ensure Xcode 15+ is installed.
macOS #
Required: Add Keychain entitlements and configure codesigning to prevent VaultStorageInitializationError.
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
- Open
macos/Runner.xcworkspacein 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 (appears with the entitlements)
3. Clean and rebuild after setup
flutter clean
flutter pub get
flutter run -d macos
4. Troubleshooting macOS Issues
If VaultStorageInitializationError persists:
- Check Console app for detailed error messages from your app
- Verify codesigning:
codesign -dv --verbose=4 /path/to/your/app.app - Reset Keychain (development only): Delete your app's keychain entries if corrupted
- Check entitlements:
codesign -d --entitlements - /path/to/your/app.app
Why this is required: VaultStorage stores encryption keys in macOS Keychain. Without entitlements and codesigning, the system blocks Keychain access and throws VaultStorageInitializationError.
Linux #
Install required system dependencies:
sudo apt-get install libsecret-1-dev libjsoncpp-dev
For runtime: libsecret-1-0 and libjsoncpp1
Windows #
No additional setup needed.
How Secure Storage Works on Windows:
- Windows uses DPAPI (Data Protection API) to encrypt the master encryption key (via
flutter_secure_storage) - The encrypted key is stored in a file at:
%APPDATA%\<app_name>\flutter_secure_storage.dat - Your data is encrypted with AES-256-GCM in Hive boxes on disk
- DPAPI ties encryption to your Windows user account, blocking other users and machines
- DPAPI storage outperforms Windows Credential Manager and scales to large apps
- The key file (
hive_encryption_key) is stored inside the encrypted.datfile, not in Windows Credential Manager
Note: readAll and deleteAll operations have limitations on Windows due to flutter_secure_storage constraints.
Debug: Finding the encrypted file on Windows:
To find the encryption key file, use the example app:
- Run the example app:
cd example && flutter run -d windows - Click the "🔍 Show Storage Location" button
- The dialog will show you the exact path, typically:
C:\Users\<username>\AppData\Roaming\<app_name>\flutter_secure_storage.dat
You can also programmatically get this path using path_provider:
import 'package:path_provider/path_provider.dart';
import 'dart:io';
final appSupportDir = await getApplicationSupportDirectory();
final encryptedKeyFile = File('${appSupportDir.path}${Platform.pathSeparator}flutter_secure_storage.dat');
print('Encrypted key location: ${encryptedKeyFile.path}');
print('File exists: ${await encryptedKeyFile.exists()}');
Web #
The package uses WebCrypto on web:
- Use HTTPS in production; consider adding Strict-Transport-Security headers
- Data is browser/domain specific and non-portable
- Auto-downloads on file retrieval
Recommended header:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Before running your app, initialise the service in 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 VaultStorageError 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),
);
}
}
Usage #
Framework-Agnostic Design #
Important: Vault Storage has no state management dependency. It works with:
- No framework: Direct instantiation and usage
- Riverpod: Integration examples below
- Bloc: Works with any Bloc pattern
- Provider: Works with Provider
- GetX: Works with GetX
- Any other solution: Use whatever suits your project
Basic Usage (No Dependencies) #
Use Vault Storage without any state management:
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 #
If you use Riverpod, create a provider:
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
Create your 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;
}
Run code generation:
dart run build_runner build
Use 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'),
);
}
}
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 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 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: getFile() auto-downloads using a filename derived from the stored extension. This API does not support custom download filenames.
Storage Classes Under the Hood #
Internally, Vault Storage keeps separate boxes for normal/secure key-value data and normal/secure files. The public API abstracts this; you provide keys and an optional isSecure flag.
Platform-Specific Behaviour #
The package handles platform differences automatically:
File Retrieval
- Web: Auto-downloads file, returns Uint8List
- Native: Returns Uint8List (no download)
File Storage
- Native platforms (iOS, Android, macOS, Windows, Linux): Files stored in the app's documents directory
- Web: Files 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, docs, audio/video, archives, JSON/TXT, etc.). Unknown types default to application/octet-stream.
No code changes needed.
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 #
All methods throw typed VaultStorageError subclasses:
try {
await vaultStorage.saveSecure(key: 'k', value: 'v');
final v = await vaultStorage.get<String>('k', isSecure: true);
} on VaultStorageInitializationError catch (e) {
debugPrint('Storage not initialised: ${e.message}');
} on VaultStorageReadError catch (e) {
debugPrint('Read failed: ${e.message}');
} on VaultStorageWriteError catch (e) {
debugPrint('Write failed: ${e.message}');
} on VaultStorageSerializationError 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 #
V3.0 Performance Improvements #
V3.0 reads Lists and Maps 20-50x faster:
Performance Gains:
- 40-item list: 1000ms → 20ms read time (50x faster)
- 100-item list: 2500ms → 50ms read time (50x faster)
- Automatic optimization - no code changes needed
Migration:
- ✅ Fully automatic - existing code works as-is
- ✅ Backward compatible - reads v2.x data without issues
- ⚠️ Cannot downgrade to v2.x after upgrade
Configurable Thresholds #
Defaults suit most workloads. Override if needed:
// Optional: customize performance thresholds at startup
VaultStorageConfig.jsonIsolateThreshold = 15000; // chars
VaultStorageConfig.base64IsolateThreshold = 100000; // bytes
VaultStorageConfig.secureFileStreamingThresholdBytes = 1 * 1024 * 1024; // 1MB
Background Processing #
Background isolates handle heavy operations:
- 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 the background
- File I/O: Large files are streamed to prevent memory spikes
The UI thread stays responsive during large operations.
Benchmarks #
Compared with managing multiple packages separately:
- 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 happens when flutter_secure_storage cannot access the platform's secure storage:
macOS: Ensure keychain entitlements are configured (see Platform Setup above)
Android: Check that minSdkVersion >= 18 and consider disabling auto-backup
Solution: Verify platform setup and restart after 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();
try {
await storage.init();
runApp(MyApp(storage: storage));
} on VaultStorageError catch (e) {
debugPrint('Storage initialisation failed: ${e.message}');
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: Text('Storage initialisation failed: ${e.message}'),
),
),
));
}
}
Web Storage Issues
For web applications:
- Ensure HTTPS is enabled for production
- Check browser compatibility with WebCrypto
- Verify that local storage is enabled
Debug Mode #
On failure, check console output — the package logs detailed error messages.
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 detection (Android/iOS only)
Platform Support #
- Android
- iOS
- Web
- macOS
- Windows
- Linux
License #
This project is licensed under the MIT License.