A comprehensive Flutter package for local storage and caching with advanced features including encryption, multi-space architecture, automatic schema migration, and high-performance query capabilities. Supports Android, iOS, macOS, Windows, Linux, and Web.
- Multi-Platform Support: Works seamlessly across Android, iOS, macOS, Windows, Linux, and Web
- Advanced Query System: SQL-like queries with chaining, nesting, joins, and complex conditions
- Multi-Space Architecture: Isolate data for different users or contexts within a single database
- Strong Encryption: AES-256-GCM and ChaCha20-Poly1305 with platform-native secure key storage
- Automatic Schema Migration: Zero-downtime migrations with intelligent field rename detection
- High Performance: Multi-level caching, batch operations, connection pooling, and prepared statements
- Backup and Restore: Full and selective backups with compression and encryption support
- Data Validation: Field-level validation with custom validators and constraints
- Monitoring: Built-in metrics, event streams, and performance tracking
- Error Recovery: Automatic retry with exponential backoff and corruption recovery
Add the dependency in your pubspec.yaml file:
dependencies:
local_storage_cache: ^2.0.0Then run:
flutter pub get| Platform | Minimum Version | Notes |
|---|---|---|
| Android | API 21 (5.0+) | Requires SQLite 3.8.0+ |
| iOS | 12.0+ | Uses SQLite.swift |
| macOS | 10.14+ | Uses SQLite.swift |
| Windows | 10+ | Requires Visual C++ Runtime |
| Linux | Ubuntu 18.04+ | Requires libsqlite3 |
| Web | Modern browsers | Uses IndexedDB |
import 'package:local_storage_cache/local_storage_cache.dart';final userSchema = TableSchema(
name: 'users',
fields: [
FieldSchema(
name: 'username',
type: DataType.text,
nullable: false,
unique: true,
),
FieldSchema(
name: 'email',
type: DataType.text,
nullable: false,
),
FieldSchema(
name: 'created_at',
type: DataType.datetime,
nullable: false,
),
],
primaryKeyConfig: const PrimaryKeyConfig(
name: 'id',
type: PrimaryKeyType.autoIncrement,
),
indexes: [
IndexSchema(
name: 'idx_username',
fields: ['username'],
),
],
);// Basic initialization
final storage = StorageEngine(
config: StorageConfig(
databaseName: 'my_app.db',
),
schemas: [userSchema],
);
await storage.initialize();
// With encryption enabled
final storage = StorageEngine(
config: StorageConfig(
databaseName: 'my_app.db',
encryption: EncryptionConfig(
enabled: true,
algorithm: EncryptionAlgorithm.aes256GCM,
useSecureStorage: true,
),
),
schemas: [userSchema],
);
await storage.initialize();final userId = await storage.insert('users', {
'username': 'john_doe',
'email': 'john@example.com',
'created_at': DateTime.now().toIso8601String(),
});// Simple query
final users = await storage.query('users')
.where('username', '=', 'john_doe')
.get();
// Complex query with multiple conditions
final activeUsers = await storage.query('users')
.where('status', '=', 'active')
.where('created_at', '>', DateTime.now().subtract(Duration(days: 30)))
.orderBy('username', ascending: true)
.limit(10)
.get();
// Query with joins
final postsWithAuthors = await storage.query('posts')
.join('users', 'posts.user_id', '=', 'users.id')
.select(['posts.*', 'users.username'])
.get();// Update using query builder
await storage.query('users')
.where('id', '=', userId)
.update({'email': 'newemail@example.com'});// Delete using query builder
await storage.query('users')
.where('id', '=', userId)
.delete();Isolate data for different users or contexts:
// Switch to a space for a specific user
await storage.switchSpace(spaceName: 'user_123');
// All operations now work within this space
await storage.insert('notes', {
'title': 'My Note',
'content': 'Note content',
});
// Switch back to default space
await storage.switchSpace(spaceName: 'default');
// Get current space name
final currentSpace = storage.currentSpace;final users = [
{'username': 'user1', 'email': 'user1@example.com'},
{'username': 'user2', 'email': 'user2@example.com'},
{'username': 'user3', 'email': 'user3@example.com'},
];
await storage.batchInsert('users', users);await storage.transaction(() async {
final userId = await storage.insert('users', {
'username': 'john_doe',
'email': 'john@example.com',
});
await storage.insert('profiles', {
'user_id': userId,
'bio': 'Software developer',
});
});For memory-efficient processing of large datasets:
// Stream records one at a time
await for (final record in storage.streamQuery('large_table')) {
await processRecord(record);
}
// Or use query builder
await for (final record in storage.query('logs').stream()) {
print(record);
}// Listen to storage events
storage.eventManager.stream.listen((event) {
print('Event: ${event.type} on table: ${event.tableName}');
});
// Get storage statistics
final stats = await storage.getStats();
print('Storage size: ${stats.storageSize} bytes');
print('Record count: ${stats.recordCount}');
print('Table count: ${stats.tableCount}');
// Get performance metrics
final metrics = storage.metricsManager.getMetrics();
print('Total queries: ${metrics.totalQueries}');
print('Average query time: ${metrics.averageQueryTime}ms');// Reclaim unused space
await storage.vacuum();
// Export database
await storage.exportDatabase('/path/to/export.db');
// Import database
await storage.importDatabase('/path/to/import.db');final config = StorageConfig(
databaseName: 'my_app.db',
databasePath: '/custom/path',
version: 1,
encryption: EncryptionConfig(
enabled: true,
algorithm: EncryptionAlgorithm.aes256GCM,
useSecureStorage: true,
),
cache: CacheConfig(
maxMemoryCacheSize: 100,
maxDiskCacheSize: 1000,
defaultTTL: Duration(hours: 1),
evictionPolicy: EvictionPolicy.lru,
enableQueryCache: true,
enableWarmCache: false,
),
performance: PerformanceConfig(
connectionPoolSize: 5,
enablePreparedStatements: true,
enableQueryOptimization: true,
enableBatchOptimization: true,
batchSize: 100,
),
logging: LogConfig(
level: LogLevel.info,
logQueries: false,
logPerformance: false,
),
enableAutoBackup: false,
enableMetrics: true,
enableEventStream: true,
);// AES-256-GCM (recommended)
final encryptionConfig = EncryptionConfig(
enabled: true,
algorithm: EncryptionAlgorithm.aes256GCM,
useSecureStorage: true,
);
// ChaCha20-Poly1305
final encryptionConfig = EncryptionConfig(
enabled: true,
algorithm: EncryptionAlgorithm.chacha20Poly1305,
useSecureStorage: true,
);
// Custom key (not recommended for production)
final encryptionConfig = EncryptionConfig(
enabled: true,
algorithm: EncryptionAlgorithm.aes256GCM,
customKey: 'your-custom-key',
);final cacheConfig = CacheConfig(
maxMemoryCacheSize: 100,
maxDiskCacheSize: 1000,
defaultTTL: Duration(hours: 1),
evictionPolicy: EvictionPolicy.lru,
enableQueryCache: true,
enableWarmCache: false,
);No additional configuration required. The package uses SQLite through the Android NDK.
To prevent issues with encryption keys, disable Android auto backup by adding the following to your android/app/src/main/AndroidManifest.xml:
<application
android:allowBackup="false"
...>
</application>Add Keychain Sharing capability to your runner. Add the following to both ios/Runner/DebugProfile.entitlements and ios/Runner/Release.entitlements for iOS, or macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlements for macOS:
<key>keychain-access-groups</key>
<array/>If using App Groups, add the App Group name:
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)your-app-group</string>
</array>Requires Visual C++ Runtime. The package uses SQLite through the Windows SDK.
Requires libsqlite3-0 to run the application. Install it using:
sudo apt-get install libsqlite3-0For development, you also need libsqlite3-dev:
sudo apt-get install libsqlite3-devThe package uses IndexedDB for storage on web platforms. Encryption is supported through WebCrypto API.
Important: The package only works on HTTPS or localhost environments for security reasons.
The package uses strongly-typed schemas to define your data structure:
final userSchema = TableSchema(
name: 'users',
fields: [
FieldSchema(
name: 'username',
type: DataType.text,
nullable: false,
unique: true,
),
FieldSchema(
name: 'email',
type: DataType.text,
nullable: false,
),
FieldSchema(
name: 'age',
type: DataType.integer,
nullable: true,
),
FieldSchema(
name: 'created_at',
type: DataType.datetime,
nullable: false,
defaultValue: 'CURRENT_TIMESTAMP',
),
],
primaryKeyConfig: const PrimaryKeyConfig(
name: 'id',
type: PrimaryKeyType.autoIncrement,
),
indexes: [
IndexSchema(
name: 'idx_username',
fields: ['username'],
unique: true,
),
IndexSchema(
name: 'idx_email',
fields: ['email'],
),
],
foreignKeys: [
ForeignKeySchema(
field: 'department_id',
referenceTable: 'departments',
referenceField: 'id',
onDelete: ForeignKeyAction.cascade,
onUpdate: ForeignKeyAction.cascade,
),
],
);The package automatically handles schema changes when you update your table definitions and increment the database version:
// Version 1 schema
final userSchemaV1 = TableSchema(
name: 'users',
fields: [
FieldSchema(name: 'username', type: DataType.text),
FieldSchema(name: 'email', type: DataType.text),
],
primaryKeyConfig: const PrimaryKeyConfig(
name: 'id',
type: PrimaryKeyType.autoIncrement,
),
);
// Version 2 schema with new field
final userSchemaV2 = TableSchema(
name: 'users',
fields: [
FieldSchema(name: 'username', type: DataType.text),
FieldSchema(name: 'email', type: DataType.text),
FieldSchema(name: 'phone', type: DataType.text), // New field
],
primaryKeyConfig: const PrimaryKeyConfig(
name: 'id',
type: PrimaryKeyType.autoIncrement,
),
);
// Initialize with new version
final storage = StorageEngine(
config: StorageConfig(
databaseName: 'my_app.db',
version: 2, // Increment version
),
schemas: [userSchemaV2],
);
await storage.initialize();The package uses connection pooling to improve performance:
final config = StorageConfig(
performance: PerformanceConfig(
connectionPoolSize: 5,
enablePreparedStatements: true,
enableQueryOptimization: true,
),
);Prepared statements are automatically cached for frequently used queries:
final config = StorageConfig(
performance: PerformanceConfig(
enablePreparedStatements: true,
),
);The package automatically optimizes queries:
final config = StorageConfig(
performance: PerformanceConfig(
enableQueryOptimization: true,
),
);Use batch operations for multiple inserts, updates, or deletes:
// Batch insert
final users = [
{'username': 'user1', 'email': 'user1@example.com'},
{'username': 'user2', 'email': 'user2@example.com'},
{'username': 'user3', 'email': 'user3@example.com'},
];
await storage.batchInsert('users', users);
// Batch update
final updates = [
{'id': 1, 'email': 'new1@example.com'},
{'id': 2, 'email': 'new2@example.com'},
];
await storage.batchUpdate('users', updates);
// Batch delete
await storage.batchDelete('users', [1, 2, 3]);The package provides comprehensive error handling with specific exception types:
try {
await storage.insert('users', {'username': 'john_doe'});
} on StorageException catch (e) {
print('Storage error: ${e.message}');
if (e.code != null) {
print('Error code: ${e.code}');
}
if (e.details != null) {
print('Details: ${e.details}');
}
} catch (e) {
print('Unexpected error: $e');
}The package includes comprehensive test utilities:
import 'package:flutter_test/flutter_test.dart';
import 'package:local_storage_cache/local_storage_cache.dart';
void main() {
late StorageEngine storage;
setUp(() async {
storage = StorageEngine(
config: StorageConfig(
databaseName: ':memory:', // In-memory database for testing
),
schemas: [userSchema],
);
await storage.initialize();
});
tearDown(() async {
await storage.close();
});
test('insert and query user', () async {
final userId = await storage.insert('users', {
'username': 'test_user',
'email': 'test@example.com',
});
final users = await storage.query('users')
.where('id', '=', userId)
.get();
expect(users.length, 1);
expect(users.first['username'], 'test_user');
});
}Complete working examples are available in the example directory:
For a complete list of available methods and configuration options, refer to the API documentation.
Contributions are welcome. To set up your development environment:
-
Clone the repository:
git clone https://github.com/mathtechstudio/local-storage-cache.git cd local-storage-cache -
Install dependencies:
flutter pub get
-
Activate Melos:
dart pub global activate melos
-
Bootstrap the workspace:
melos bootstrap
-
Run tests:
melos test
Please read the contributing guidelines before submitting pull requests.
This project is licensed under the MIT License. See the LICENSE file for details.
- Report issues on GitHub Issues
- Ask questions on GitHub Discussions
- View the CHANGELOG for version history
This package uses SQLite for local storage and implements platform-specific secure storage mechanisms for encryption key management.