vex_storage 1.0.0
vex_storage: ^1.0.0 copied to clipboard
The ultimate Flutter/Dart storage solution. Zero external dependencies. Blazing-fast key-value store + full SQL-like query engine + reactive streams + optional AES-256 encryption. Replaces GetStorage, [...]
import 'package:flutter/material.dart';
import 'package:vex_storage/vex_storage.dart';
/// Full example demonstrating VexStorage features.
///
/// Run with: flutter run -d <device> example/lib/main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 1️⃣ Initialise — provide your app's documents directory.
// For a real app: `(await getApplicationDocumentsDirectory()).path`
await Vex.init(
options: const VexOptions(
inMemory: true, // ← use inMemory: false + directory for persistence
// cipher: VexCipher.fromPassword('my-secret-password'), // optional AES-256
),
);
runApp(const VexExampleApp());
}
class VexExampleApp extends StatelessWidget {
const VexExampleApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'VexStorage Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const VexHomePage(),
);
}
class VexHomePage extends StatefulWidget {
const VexHomePage({super.key});
@override
State<VexHomePage> createState() => _VexHomePageState();
}
class _VexHomePageState extends State<VexHomePage> {
late VexCollection _users;
List<VexDocument> _docs = [];
String _log = '';
bool _ready = false;
@override
void initState() {
super.initState();
_setup();
}
Future<void> _setup() async {
_users = await Vex.collection('users');
_users.createIndex('age');
// Reactive: rebuild UI on any change.
_users.watch().listen((_) => _refresh());
setState(() => _ready = true);
}
void _refresh() {
setState(() {
_docs = _users.query().sortBy('name').findAll() as List<VexDocument>;
});
}
void _addLog(String msg) => setState(() => _log = msg);
// ── Demo actions ────────────────────────────────────────────────────────────
Future<void> _seedData() async {
await _users.putMany([
{'name': 'Alice', 'age': 30, 'city': 'London', 'role': 'admin'},
{'name': 'Bob', 'age': 25, 'city': 'Paris', 'role': 'user'},
{'name': 'Charlie', 'age': 35, 'city': 'London', 'role': 'admin'},
{'name': 'Diana', 'age': 28, 'city': 'Berlin', 'role': 'user'},
{'name': 'Eve', 'age': 22, 'city': 'London', 'role': 'user'},
]);
_addLog('Seeded 5 users');
}
Future<void> _queryLondon() async {
final r = _users
.query()
.where('city').eq('London')
.sortBy('age')
.findAll() as List<VexDocument>;
_addLog('London users (${r.length}): ${r.map((d) => d.get<String>('name')).join(', ')}');
}
Future<void> _queryAdultsInLondon() async {
final r = _users
.query()
.where('city').eq('London')
.and('age').gte(28)
.sortByDesc('age')
.findAll() as List<VexDocument>;
_addLog(
'London adults ≥28 (${r.length}): ${r.map((d) => '${d.get<String>('name')}(${d.get<int>('age')})').join(', ')}');
}
Future<void> _aggregation() async {
final avg = _users.avgOf('age');
final max = _users.maxOf('age');
final groups = _users.groupBy('city');
_addLog(
'Avg age: ${avg?.toStringAsFixed(1)}, Max: $max, Cities: ${groups.keys.join(', ')}');
}
Future<void> _keyValueDemo() async {
final prefs = await Vex.collection('prefs');
await prefs.write('theme', 'dark');
await prefs.write('fontSize', 16);
final theme = prefs.read<String>('theme');
final size = prefs.read<int>('fontSize');
_addLog('Prefs → theme: $theme, fontSize: $size');
}
Future<void> _clearAll() async {
await _users.clear();
_addLog('Cleared all users');
}
@override
Widget build(BuildContext context) {
if (!_ready) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));
}
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('⚡ VexStorage Demo'),
),
body: Column(
children: [
// ── Action buttons ────────────────────────────────────────────────
Wrap(
spacing: 8,
runSpacing: 4,
children: [
_Btn('Seed data', _seedData),
_Btn('Query London', _queryLondon),
_Btn('Adults in London', _queryAdultsInLondon),
_Btn('Aggregation', _aggregation),
_Btn('Key-Value demo', _keyValueDemo),
_Btn('Clear', _clearAll, color: Colors.red),
],
),
// ── Log ───────────────────────────────────────────────────────────
if (_log.isNotEmpty)
Container(
width: double.infinity,
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.deepPurple.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Text(_log,
style: const TextStyle(fontFamily: 'monospace', fontSize: 12)),
),
const Divider(),
// ── Document list ─────────────────────────────────────────────────
Expanded(
child: _docs.isEmpty
? const Center(
child: Text('No users yet — tap "Seed data"',
style: TextStyle(color: Colors.grey)))
: ListView.builder(
itemCount: _docs.length,
itemBuilder: (ctx, i) {
final doc = _docs[i];
return ListTile(
leading: CircleAvatar(
backgroundColor: doc.get<String>('role') == 'admin'
? Colors.deepPurple
: Colors.teal,
child: Text(
doc.get<String>('name')?.substring(0, 1) ?? '?',
style:
const TextStyle(color: Colors.white)),
),
title: Text(doc.get<String>('name') ?? ''),
subtitle: Text(
'${doc.get<String>('city')} · age ${doc.get<int>('age')}'),
trailing: Chip(
label: Text(doc.get<String>('role') ?? ''),
backgroundColor:
doc.get<String>('role') == 'admin'
? Colors.deepPurple.shade100
: Colors.teal.shade100,
),
onLongPress: () => _users.delete(doc.id),
);
},
),
),
],
),
);
}
}
class _Btn extends StatelessWidget {
final String label;
final VoidCallback onTap;
final Color? color;
const _Btn(this.label, this.onTap, {this.color});
@override
Widget build(BuildContext context) => ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: color, foregroundColor: Colors.white),
onPressed: onTap,
child: Text(label),
);
}