
I released SQLiteNow for Flutter/Dart: SQL-first, type-safe SQLite codegen
Hey Flutter folks,
I just published the first Dart/Flutter release of SQLiteNow. If you used SQLDelight before with Kotlin Multiplatform - you would feel right at home.
Short version: SQLiteNow lets you keep your database schema and queries as normal .sql files, then generates typed Dart code around them: params, result models, migrations, transactions, query namespaces, and reactive watch() APIs.
It is not trying to hide SQLite behind a big ORM. The idea is closer to: write real SQL, keep full control over the database, but stop manually mapping rows and passing dynamic parameter maps everywhere. No plugins needed, you write your code in any editor that supports .sql files, you just use your knowledge of SQL and SQLite. Additionally there are annotations available as comments --@@{...} to shape and control data generated.
The part I personally care about is that SQLiteNow is not trying to make SQLite disappear. You still write normal SQLite:
-- @@{ queryResult=TaskWithTags }
SELECT
t.id,
t.title,
t.completed,
tag.id AS tag__id,
tag.name AS tag__name
/* @@{ dynamicField=tags,
mappingType=collection,
propertyType=List<TaskTag>,
sourceTable=tag,
collectionKey=tag__id } */
FROM task t
LEFT JOIN task_tag tt ON tt.task_id = t.id
LEFT JOIN tag ON tag.id = tt.tag_id
ORDER BY t.id, tag.name;
and SQLiteNow generates the Dart result type and query method around it. So instead of hand-reading rows, building maps, or doing a second pass to group tags under tasks, you get a typed result shaped like your app actually wants:
final tasks = await db.task.selectWithTags().asList();
for (final task in tasks) {
print('${task.title}: ${task.tags.map((t) => t.name).join(', ')}');
}
That is the main difference from using sqlite3/sqflite directly: I still get real SQL, but not all the manual row mapping.
And yes, when you use a watch() - any changes to `task` or `tag` tables will reactively update your data.
Compared with higher-level database libraries, the design goal is a bit different too. SQLiteNow is very SQLite-specific and SQL-file-first. Schema, migrations, seed data, and queries live as SQL assets, and annotations are just SQL comments. I wanted the generated code to stay close to my domain model without moving the real database logic into a Dart DSL or ORM layer.
A bit of context: SQLiteNow originally started as a Kotlin Multiplatform project and KMP version is used in production by quite a few of people. And now Dart/Flutter packages are published on pub.dev.
There is also an optional sqlitenow_oversqlite package for sync/multi-device work, but you can ignore that completely if you only want local SQLite.
Current honest status:
- first public Dart/Flutter release, 0.9.0
- targets Dart VM and Flutter native runtimes through package:sqlite3
- web support is not public yet
- docs may still show some KMP history, but the Flutter path is there now
Links:
- GitHub: https://github.com/mobiletoly/sqlitenow-kmp
- Flutter docs: https://mobiletoly.github.io/sqlitenow-kmp/flutter/
- Runtime: https://pub.dev/packages/sqlitenow_runtime
- CLI: https://pub.dev/packages/sqlitenow_cli
- Optional sync runtime: https://pub.dev/packages/sqlitenow_oversqlite
I'd really appreciate feedback from people who build SQLite-heavy Flutter apps. Especially if you've used drift, sqflite, or handwritten sqlite3 code and have opinions about where this approach feels useful or annoying.