resqlite

High-performance, reactive SQLite for Dart & Flutter.
Plain SQL. Stream anything. Zero main-isolate jank.

Reactive queries · auto invalidation iOS · Android · macOS · Linux · Windows Benchmark-driven design No ORM · No codegen
faster select()
(1K rows)
faster reactive feed
(100 concurrent writes)
main-thread cost
(10K row read)
point queries
per second

Compared against peer Dart SQLite libraries on the latest published benchmark run. See full benchmarks →

📖

API Reference

Full dartdoc-generated documentation for every public class, method, and property.

Benchmarks

Interactive charts comparing resqlite, sqlite3, and sqlite_async across reads, writes, streaming, and more.

🔬

Experiments

Optimization experiments with performance-over-time charts. Click any data point to see what changed.

📝

Blog

Engineering stories, architecture deep dives, and generated experiment posts behind the library's design.

🛠

Source Code

Browse the repo, open issues, and contribute on GitHub.

🚀

Quickstart

Add the package, open a database, run plain SQL, and subscribe to reactive queries.

Install and run the first query

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.

Database API →
1 Add the dependency

Use a local path dependency until the package is published.

// pubspec.yaml
dependencies:
  resqlite:
    path: ../resqlite
2 Open and create schema

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
  )
''');
3 Read, write, stream

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}');
});

Quick look

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'},
);