Code Generation
Generate typed tables from annotated models with flutter_instantdb_generator
Instead of hand-writing InstantTable classes, you can annotate a plain model class
and let the build-time generator (Phase 6b) emit a typed table, columns, a fromRow
mapper, and getAll / watchAll query helpers.
Annotations
@InstantModel(entityType)— marks a class as a model.entityTypeis the namespace the generated table queries (e.g.'todos').@InstantField(name)— overrides the stored attribute name for a field. Without it, the field name is used as the attribute name.@InstantLink()— marks a relation field; see Relations.
import 'package:flutter_instantdb/flutter_instantdb.dart';
part 'sample.instant.dart';
@InstantModel('gadgets')
class Gadget {
final String id;
final String label;
const Gadget({required this.id, required this.label});
}Flat models (primitive fields) are fully supported. Relation/nested fields use
@InstantLink (see Relations); non-nullable relations are rejected by
the generator with guidance.
The generator package
The generator ships as a separate dev-only package,
flutter_instantdb_generator, built on build_runner / source_gen. Add it (plus
build_runner) to your dev_dependencies:
dev_dependencies:
build_runner: ^2.4.13
flutter_instantdb_generator: ^0.1.0It is configured with auto_apply: dependents and build_to: source, so it runs on
any package that depends on it and writes .instant.dart files next to your sources.
Running the generator
Make sure your model file has a part 'your_file.instant.dart'; directive, then run:
dart run build_runner build(Use dart run build_runner watch to regenerate on save.)
Generated output
For the model above, the generator emits a ${Model}Table with a Col<T> per field,
a fromRow, a scalar-only toMap, a tx(db) convenience, plus a ${Model}QueryX
extension carrying getAll / watchAll and a ${Model}TxX extension for whole-model
writes:
class GadgetTable extends InstantModelTable<GadgetTable, Gadget> {
GadgetTable() : super('gadgets');
final id = const Col<String>('id');
final label = const Col<String>('label');
@override
Gadget fromRow(Map<String, dynamic> m) => Gadget(
id: m['id'] as String,
label: m['label'] as String,
);
Map<String, dynamic> toMap(Gadget m) => {
'id': m.id,
'label': m.label,
};
TypedTx<GadgetTable> tx(InstantDB db) => db.txFor(this);
}
extension GadgetQueryX on TypedQuery<GadgetTable> {
Future<List<Gadget>> getAll(InstantDB db) async =>
(await db.queryOnceTyped(this))
.documents
.map(GadgetTable().fromRow)
.toList();
ReadonlySignal<List<Gadget>> watchAll(InstantDB db) {
final src = db.queryTyped(this);
return computed(
() => src.value.documents.map(GadgetTable().fromRow).toList());
}
}Using the generated table
The generated table behaves like any typed table, and getAll /
watchAll return typed List<Model> instead of raw maps:
// One-shot, typed list
final gadgets = await GadgetTable().query().getAll(db);
// gadgets is List<Gadget>
// Reactive, typed list
final signal = GadgetTable()
.query()
.where((t) => t.label.ilike('%pro%'))
.watchAll(db);
Watch((context) {
final gadgets = signal.value; // List<Gadget>
return Text('${gadgets.length} gadgets');
});For whole-model writes (createModel / updateModel / mergeModel) and the
table.tx(db) sugar, see Transactions.