High-performance, reactive SQLite for Dart & Flutter.
Plain SQL. Stream anything. Zero main-isolate jank.
Compared against peer Dart SQLite libraries on the latest published benchmark run. See full benchmarks →
Full dartdoc-generated documentation for every public class, method, and property.
⚡Interactive charts comparing resqlite, sqlite3, and sqlite_async across reads, writes, streaming, and more.
🔬Optimization experiments with performance-over-time charts. Click any data point to see what changed.
📝Engineering stories, architecture deep dives, and generated experiment posts behind the library's design.
🛠Browse the repo, open issues, and contribute on GitHub.
🚀Add the package, open a database, run plain SQL, and subscribe to reactive queries.
resqlite is currently source-only, so use a path dependency while the package is under active development. Native assets compile automatically for Dart and Flutter targets.
Use a local path dependency until the package is published.
// pubspec.yaml
dependencies:
resqlite:
path: ../resqlite
Open an on-device SQLite database and manage schema with plain SQL.
// Dart / Flutter import 'package:resqlite/resqlite.dart'; final db = await Database.open('app.db'); await db.execute(''' CREATE TABLE IF NOT EXISTS todos ( id INTEGER PRIMARY KEY, title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0 ) ''');
Queries and writes run off the main isolate; streams re-query automatically.
// Insert, query, then subscribe await db.execute( 'INSERT INTO todos(title) VALUES (?)', ['Ship a smooth local-first UI'], ); final openTodos = await db.select( 'SELECT * FROM todos WHERE done = ?', [0], ); db.stream('SELECT * FROM todos ORDER BY id DESC').listen((rows) { print('todos changed: ${rows.length}'); });
final db = await Database.open('app.db'); // Queries run on background isolates — zero UI jank. final users = await db.select( 'SELECT * FROM users WHERE active = ?', [1], ); // Row objects are lazy views — only accessed rows get materialized. for (final user in users) { print('${user['name']}: ${user['email']}'); }
// Single write — runs on a dedicated writer isolate. final result = await db.execute( 'INSERT INTO users(name, email) VALUES (?, ?)', ['Ada', 'ada@example.com'], ); print('Inserted row ${result.lastInsertId}'); // Batch write — one prepare, one commit, no per-row overhead. await db.executeBatch( 'INSERT INTO users(name) VALUES (?)', [['Ada'], ['Grace'], ['Sonja']], );
// Turn any query into a live stream. // Table dependencies detected automatically — works with // JOINs, subqueries, views, and CTEs. db.stream( 'SELECT * FROM users WHERE active = ?', [1], ).listen((users) { setState(() => this.users = users); }); // Identical queries are deduplicated. // Unchanged results are suppressed. // Re-queries fire on write commit — sub-millisecond.
// Atomic transactions — reads see uncommitted writes. final count = await db.transaction((tx) async { await tx.execute( 'INSERT INTO users(name) VALUES (?)', ['Sonja'], ); final rows = await tx.select( 'SELECT COUNT(*) as c FROM users', ); return rows.first['c']; }); // Nested transactions use SQLite SAVEPOINTs.
// JSON serialized entirely in C — zero Dart object allocation. // Perfect for HTTP server responses. final bytes = await db.selectBytes( 'SELECT id, name, price FROM products WHERE active = ?', [1], ); // bytes is a Uint8List of JSON — ready to write to a socket. return Response.ok( bytes, headers: {'content-type': 'application/json'}, );