Package selection guidance compiled from public package information, community data, and project usage.
Sqlcool is a Flutter/Dart package that simplifies working with SQLite databases by providing reactive streams for database changes, built-in select bloc patterns, and support for custom database models. It enables real-time data synchronization and efficient querying with minimal boilerplate. Designed for developers seeking a lightweight, intuitive solution for persistent local data storage in Flutter apps, Sqlcool integrates seamlessly with reactive programming paradigms. The package emphasizes ease of use while maintaining flexibility for complex database operations.
Use cases
Real-time data sync in offline-first apps
Reactive UI updates based on database changes
Managing local state with structured database models
Building forms with dynamic data loading
Implementing caching layers with SQLite
Simplifying CRUD operations with custom models
Key features
Reactive streams for database change notifications
Built-in select bloc pattern for state management
Support for custom database model definitions
Simple API for queries and mutations
Automatic type-safe model mapping
Lightweight and minimal dependencies
Suitable for
Developers building offline-capable Flutter apps
Teams using reactive state management
Projects requiring real-time database updates
Apps needing structured local data persistence
Beginners learning SQLite in Flutter
Apps with frequent local data interactions
Considerations
Limited to SQLite only (no other databases)
No built-in migration handling (manual or external tools required)
May require additional setup for complex schema changes
Not ideal for large-scale server-side database operations
Best suited for mobile and desktop use cases
Requires understanding of reactive programming concepts
Original README
Original README snapshot from the linked source repository. Visit the repository for the latest content.
Sqlcool
A database helper library for Sqflite. Forget about implementation details and focus on the business logic.
A stream of database change events is available. Inspired by Rethinkdb
import'dart:async';
import'package:sqlcool/sqlcool.dart';
StreamSubscription changefeed;
changefeed = db.changefeed.listen((change) {
print("Change in the database:");
print("Query: ${change.query}");
if (change.type == DatabaseChange.update) {
print("${change.value} items updated");
}
});
// Dispose the changefeed when finished using it
changefeed.cancel();
Reactive select bloc
The bloc will rebuild itself on any database change because of the reactive
parameter set to true:
import'package:flutter/material.dart';
import'package:sqlcool/sqlcool.dart';
class_PageSelectBlocStateextendsState<PageSelectBloc> {
SelectBloc bloc;
@overridevoidinitState() {
super.initState();
this.bloc = SelectBloc(
table: "items", orderBy: "name", reactive: true);
}
@overridevoiddispose() {
bloc.dispose();
super.dispose();
}
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
appBar: AppBar(title: Text("My app")),
body: StreamBuilder<List<Map>>(
stream: bloc.items,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// the select query has not found anythingif (snapshot.data.length == 0) {
returnCenter(child: constText("No data"));
}
// the select query has resultsreturnListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
var item = snapshot.data[index];
returnListTile(
title: GestureDetector(
child: Text(item["name"]),
onTap: () => someFunction()),
);
});
} else {
// the select query is still runningreturnCircularProgressIndicator();
}
}),
);
}
}
classPageSelectBlocextendsStatefulWidget{
@override_PageSelectBlocStatecreateState() => _PageSelectBlocState();
}
Database models
New in 4.0.0: define models that have database methods. The main
advantage of this is to use only typed model data and avoid the
type conversions from maps for every query. It directly plugs custom
models into the database. Example:
In schema.dart:
final carTable = DbTable("car")
..varchar("name")
..integer("max_speed")
..real("price")
..integer("year")
..boolean("is_4wd", defaultValue: false)
..foreignKey("manufacturer", onDelete: OnDelete.cascade);
final manufacturerTable = DbTable("manufacturer")..varchar("name");
In car_model.dart:
import'package:sqlcool/sqlcool.dart';
// the database schemaimport'schema.dart';
// another modelimport'manufacturer_model.dart';
classCarwithDbModel{
Car(
{this.id,
this.name,
this.maxSpeed,
this.price,
this.year,
this.is4wd,
this.manufacturer});
/// define some class propertiesfinalString name;
finalint maxSpeed;
finaldouble price;
finalDateTime year;
finalbool is4wd;
// this is a foreign key to another modelManufacturer manufacturer;
/// [DbModel] required overrides@overrideint id;
/// the [Db] used/// pass it your main db@overrideDbget db => db;
/// the table schema representation/// check example/pages/dbmodels/schema.dart@overrideDbTableget table => carTable;
/// serialize a row to the database@overrideMap<String, dynamic> toDb() {
// we want the foreign key to be recordedassert(manufacturer?.id != null);
final row = <String, dynamic>{
"name": name,
"max_speed": maxSpeed,
"price": price,
"year": year.millisecondsSinceEpoch,
"is_4wd": is4wd,
"manufacturer": manufacturer.id
};
return row;
}
/// deserialize a row from database@overrideCarfromDb(Map<String, dynamic> map) {
final car = Car(
id: map["id"] asint,
name: map["name"].toString(),
maxSpeed: map["max_speed"] asint,
price: map["price"] asdouble,
year: DateTime.fromMillisecondsSinceEpoch(map["year"] asint),
is4wd: (map["is_4wd"].toString() == "true"),
);
// the key will be present only with join queries// in a simple select this data is not presentif (map.containsKey("manufacturer")) {
car.manufacturer =
Manufacturer().fromDb(map["manufacturer"] asMap<String, dynamic>);
}
return car;
}
/// Create a static join method for conveniencestaticFuture<List<Car>> selectRelated({String where, int limit}) async {
final cars = List<Car>.from(
awaitCar().sqlJoin(where: where, limit: limit, verbose: true));
return cars;
}
}
Then use the models:
/// car is an instance of [Car]await car.sqlInsert();
await car.sqlUpdate();
await car.sqlUpsert();
await car.sqlDelete();
final cars = Car.selectRelated(where: "speed>200");
// foreign keys are retrieved as model instancesprint(cars[0].manufacturer.name);