quantum_cache 1.0.0
quantum_cache: ^1.0.0 copied to clipboard
A high-performance pure Dart cache with L1 memory + L2 disk layers, offering fast access, adaptive ARC eviction, TTL support, AES-256-GCM encryption, isolate safety, and zero native dependencies.
example/lib/main.dart
// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:quantum_cache/quantum_cache.dart';
// ─────────────────────────────────────────────────────────────────────────────
// Custom Model + Adapter
// ─────────────────────────────────────────────────────────────────────────────
/// Example domain model.
class UserProfile {
const UserProfile({
required this.id,
required this.name,
required this.email,
required this.age,
required this.createdAt,
required this.tags,
});
final String id;
final String name;
final String email;
final int age;
final DateTime createdAt;
final List<String> tags;
@override
String toString() =>
'UserProfile(id: $id, name: $name, age: $age, tags: $tags)';
}
/// Hand-written adapter (normally generated by quantum_cache_builder).
class UserProfileAdapter extends SuperCacheAdapter<UserProfile> {
@override
int get typeId => 1;
@override
UserProfile read(BinaryReader reader) {
return UserProfile(
id: reader.readString(),
name: reader.readString(),
email: reader.readString(),
age: reader.readInt32(),
createdAt:
DateTime.fromMicrosecondsSinceEpoch(reader.readInt64(), isUtc: true),
tags: List.generate(reader.readUint32(), (_) => reader.readString()),
);
}
@override
void write(BinaryWriter writer, UserProfile obj) {
writer
..writeString(obj.id)
..writeString(obj.name)
..writeString(obj.email)
..writeInt32(obj.age)
..writeInt64(obj.createdAt.microsecondsSinceEpoch)
..writeUint32(obj.tags.length);
for (final tag in obj.tags) {
writer.writeString(tag);
}
}
}
// ─────────────────────────────────────────────────────────────────────────────
// App Entry Point
// ─────────────────────────────────────────────────────────────────────────────
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// ── 1. Register custom adapters BEFORE init ──────────────────────────────
SuperCache.registerAdapter(UserProfileAdapter());
// ── 2. Initialize the cache ───────────────────────────────────────────────
await SuperCache.init(
config: const CacheConfig(
maxMemoryEntries: 5000,
maxMemorySizeBytes: 100 * 1024 * 1024, // 100 MB L1
maxDiskSizeBytes: 512 * 1024 * 1024, // 512 MB L2
defaultTtl: Duration(hours: 24),
evictionPolicy: EvictionPolicyType.arc,
writeBufferSize: 200,
writeBufferFlushInterval: Duration(milliseconds: 300),
enableStatistics: true,
enableLogging: true,
logLevel: CacheLogLevel.info,
),
);
runApp(const SuperCacheExampleApp());
}
// ─────────────────────────────────────────────────────────────────────────────
// App Widget
// ─────────────────────────────────────────────────────────────────────────────
class SuperCacheExampleApp extends StatelessWidget {
const SuperCacheExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'quantum_cache Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const CacheDemoPage(),
);
}
}
// ─────────────────────────────────────────────────────────────────────────────
// Demo Page
// ─────────────────────────────────────────────────────────────────────────────
class CacheDemoPage extends StatefulWidget {
const CacheDemoPage({super.key});
@override
State<CacheDemoPage> createState() => _CacheDemoPageState();
}
class _CacheDemoPageState extends State<CacheDemoPage> {
final _cache = SuperCache.instance;
final _log = <String>[];
void _appendLog(String msg) {
setState(() => _log.insert(0, msg));
}
// ── Demo Operations ────────────────────────────────────────────────────────
Future<void> _demoBasicTypes() async {
await _cache.put('username', 'Alice');
await _cache.put('score', 9500);
await _cache.put('isPremium', true);
await _cache.put('balance', 3.14159);
await _cache.put('lastLogin', DateTime.now().toUtc());
final name = await _cache.get<String>('username');
final score = await _cache.get<int>('score');
final premium = await _cache.get<bool>('isPremium');
_appendLog('📦 Basic types: name=$name, score=$score, premium=$premium');
}
Future<void> _demoCustomObject() async {
final user = UserProfile(
id: 'u001',
name: 'Bob Smith',
email: 'bob@example.com',
age: 32,
createdAt: DateTime.now().toUtc(),
tags: ['admin', 'verified', 'premium'],
);
await _cache.put('user:u001', user, ttl: const Duration(hours: 2));
final loaded = await _cache.get<UserProfile>('user:u001');
_appendLog('👤 Custom object: ${loaded?.name}, tags: ${loaded?.tags}');
}
Future<void> _demoCacheAside() async {
var loaderCalls = 0;
final products = await _cache.getOrPut<List<dynamic>>(
'product_catalog',
() async {
loaderCalls++;
// Simulate network call
await Future<void>.delayed(const Duration(milliseconds: 100));
return ['Product A', 'Product B', 'Product C', 'Product D'];
},
ttl: const Duration(minutes: 30),
);
// Second call — should NOT call loader
await _cache.getOrPut<List<dynamic>>(
'product_catalog',
() async => throw Exception('Loader should not be called!'),
);
_appendLog('🗂 Cache-aside: ${products.length} products, '
'loader called: $loaderCalls time(s)');
}
Future<void> _demoBatch() async {
await _cache.putAll({
'city:1': 'New York',
'city:2': 'London',
'city:3': 'Tokyo',
'city:4': 'Dubai',
}, ttl: const Duration(days: 7));
final cities = await _cache.getAll(['city:1', 'city:2', 'city:3', 'city:4']);
_appendLog('🌍 Batch get: ${cities.values.join(', ')}');
}
Future<void> _demoTtl() async {
await _cache.put(
'session_token',
'tok_abc123xyz',
ttl: const Duration(seconds: 3),
);
final before = await _cache.get<String>('session_token');
_appendLog('⏳ Token before expiry: $before');
await Future<void>.delayed(const Duration(seconds: 4));
final after = await _cache.get<String>('session_token');
_appendLog('⌛ Token after expiry: $after (null = expired)');
}
Future<void> _demoEncryption() async {
// Generate a key (in production, derive from flutter_secure_storage)
final key = AesCipher.generateKey();
await SuperCache.init(
name: 'secure',
config: CacheConfig(
boxName: 'secure_box',
encryptionKey: key,
maxMemoryEntries: 100,
),
);
final secureCache = SuperCache.named('secure');
await secureCache.put('credit_card', '4111-1111-1111-1111');
final card = await secureCache.get<String>('credit_card');
_appendLog('🔐 Encrypted: card ends with ${card?.split('-').last}');
}
Future<void> _showStatistics() async {
final stats = _cache.statistics.snapshot();
_appendLog(
'📊 Stats: hitRate=${(stats.hitRate * 100).toStringAsFixed(1)}%, '
'L1=${stats.l1Hits}, L2=${stats.l2Hits}, '
'miss=${stats.misses}, writes=${stats.writes}',
);
}
Future<void> _warmUp() async {
// Pre-seed some data
for (var i = 0; i < 10; i++) {
await _cache.put('warmup:$i', 'value_$i', forceSync: true);
}
// Simulate L1 being empty (e.g. after app restart — L2 has data)
_cache.statistics.reset();
// Warm up L1 from L2
await _cache.warmUp(List.generate(10, (i) => 'warmup:$i'));
_appendLog('🔥 Warmed up 10 keys into L1');
}
// ─────────────────────────────────────────────────────────────────────────
// UI
// ─────────────────────────────────────────────────────────────────────────
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('⚡ quantum_cache Demo'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Column(
children: [
// ── Controls ─────────────────────────────────────────────────────
Wrap(
spacing: 8,
runSpacing: 8,
children: [
_demoButton('📦 Basic Types', _demoBasicTypes),
_demoButton('👤 Custom Object', _demoCustomObject),
_demoButton('🗂 Cache-Aside', _demoCacheAside),
_demoButton('🌍 Batch Ops', _demoBatch),
_demoButton('⏳ TTL Demo', _demoTtl),
_demoButton('🔐 Encryption', _demoEncryption),
_demoButton('📊 Statistics', _showStatistics),
_demoButton('🔥 Warm-Up', _warmUp),
_demoButton('🗑 Clear Cache', () async {
await _cache.clear();
_appendLog('🗑 Cache cleared');
}),
],
),
const Divider(),
// ── Log ──────────────────────────────────────────────────────────
Expanded(
child: _log.isEmpty
? const Center(
child: Text(
'Tap a button to run a demo',
style: TextStyle(color: Colors.grey),
),
)
: ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: _log.length,
itemBuilder: (_, i) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Text(
_log[i],
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 13,
),
),
),
),
),
],
),
);
}
Widget _demoButton(String label, Future<void> Function() action) {
return ElevatedButton(
onPressed: () async {
try {
await action();
} catch (e) {
_appendLog('❌ Error: $e');
}
},
child: Text(label),
);
}
@override
void dispose() {
_cache.dispose();
super.dispose();
}
}