vault_storage 0.0.1
vault_storage: ^0.0.1 copied to clipboard
A package for secure key-value and file storage using Hive and flutter_secure_storage.
Vault Storage #
A secure and performant local storage solution for Flutter applications, built with Hive, Flutter Secure Storage, and Riverpod. It provides both key-value storage and encrypted file storage, with intensive cryptographic operations offloaded to background isolates to ensure a smooth UI.
Note: This package depends on Riverpod v3 (
riverpod: ^3.0.0-dev), which is currently in pre-release. Be mindful of potential API changes in future Riverpod updates.
Features #
- Dual Storage Model: Simple key-value storage via Hive and secure file storage for larger data blobs (e.g., images, documents).
- Robust Security: Utilizes
flutter_secure_storageto protect the master encryption key, an encrypted Hive box for sensitive key-value pairs, and per-file encryption for file storage. - High Performance: Cryptographic operations (AES-GCM) are executed in background isolates using
computeto prevent UI jank. - Type-Safe Error Handling: Leverages
fpdart'sEitherandTaskEitherfor explicit, functional-style error management. - Ready for Dependency Injection: Comes with a pre-configured Riverpod provider for easy integration and lifecycle management.
Getting Started #
This is a local package. To use it in your main application, add it as a path dependency in your pubspec.yaml:
dependencies:
# ... other dependencies
vault_storage:
path: packages/vault_storage
Before running your app, you must initialize the service. This is typically done in your main.dart or an initialization module.
// In your main function or an initialization class
import 'package:vault_storage/vault_storage.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Create a ProviderContainer to access the provider.
final container = ProviderContainer();
// Initialize the vault storage.
await container.read(storageServiceProvider.future);
runApp(
UncontrolledProviderScope(
container: container,
child: const MyApp(),
),
);
}
Usage #
Key-Value Storage #
You can store and retrieve simple key-value pairs using the set and get methods. The BoxType enum determines whether the data is stored in the encrypted secure box or the unencrypted normal box.
final storageService = await container.read(storageServiceProvider.future);
// Store a secure value
await storageService.set(BoxType.secure, 'api_key', 'my_secret_key');
// Retrieve a secure value
final apiKey = await storageService.get<String>(BoxType.secure, 'api_key');
apiKey.fold(
(error) => print('Error retrieving key: ${error.message}'),
(key) => print('Retrieved API key: $key'),
);
Secure File Storage #
For larger data like images or documents, you can use the secure file storage methods.
import 'dart:typed_data';
// Assume 'imageData' is a Uint8List
final storageService = await container.read(storageServiceProvider.future);
// Save a file
final saveResult = await storageService.saveSecureFile(
fileBytes: imageData,
fileExtension: 'png',
);
saveResult.fold(
(error) => print('Error saving file: ${error.message}'),
(metadata) async {
print('File saved successfully. Metadata: $metadata');
// Retrieve the file
final getResult = await storageService.getSecureFile(fileMetadata: metadata);
getResult.fold(
(error) => print('Error retrieving file: ${error.message}'),
(fileBytes) => print('Retrieved file with ${fileBytes.length} bytes.'),
);
},
);
Usage without Riverpod #
If you are not using Riverpod, you can instantiate and manage the StorageService directly.
1. Initialization #
You'll need to create an instance of StorageService and initialize it when your application starts. You can store the instance in a global variable or use a service locator pattern (like get_it).
// In your main.dart or an initialization file
import 'package:vault_storage/vault_storage.dart';
// Using a global variable for simplicity.
late final StorageService storageService;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Instantiate and initialize the service.
storageService = StorageService();
await storageService.init(); // You must call init() before using the service.
runApp(const MyApp());
}
2. Accessing the Service #
Once initialized, you can use your storageService instance anywhere in your app to call its methods. The API is identical to the one used with Riverpod.
// Example of using the manually created instance:
// Store a secure value
await storageService.set(BoxType.secure, 'api_key', 'my_secret_key');
// Retrieve a secure value
final apiKey = await storageService.get<String>(BoxType.secure, 'api_key');
apiKey.fold(
(error) => print('Error retrieving key: ${error.message}'),
(key) => print('Retrieved API key: $key'),
);
Error Handling #
The service uses fpdart's Either for error handling. All methods return an Either<StorageError, T>, where T is the success type. This forces you to handle potential failures explicitly.
Testing #
This package includes a comprehensive test suite. To run the tests, use the following command:
flutter test
License #
This project is licensed under the MIT License.