Skip to main content
A Cindel collection is the Dart class that becomes a stored table in your database. You annotate the class, declare its fields, run code generation, and Cindel produces a typed schema and query API ready to use in your Flutter app. This page walks through every modeling decision you will encounter: collection annotations, IDs, persisted field types, ignored fields, enums, and indexes.

Declaring a Collection

Use @Collection() to mark a Dart class as a Cindel root collection. Pass a name: argument to set an explicit stored name, or omit it to let the generator derive the name from the class name.
After code generation this model produces:
  • a schema constant (UserSchema),
  • a typed collection accessor on the database instance (db.users),
  • generated read, write, and query helpers for User.
The name parameter is the persisted collection name. Cindel stores it with the database and uses it for every open, migration, backup, and restore. Keep it stable once you have real user data — renaming the Dart class is safe, but changing the stored name changes the format your database expects.

@collection Shorthand

When you are happy with a derived name, use the lowercase @collection constant — it is equivalent to @Collection() with no arguments.
Cindel derives the collection name from the class name. This is fine for prototypes and tests. Prefer @Collection(name: ...) when you want the stored name to be explicit and stable.

Controlling Stored Names with @Name

Use @Name to decouple the stored name from the Dart identifier on either a class or a field.
In Dart you still reference Account, AccountSchema, db.accounts, and username. In storage the collection is named accounts and the field is named user_name. This lets you improve Dart naming freely while keeping the stored format stable for existing data.
Every Cindel collection class requires a part directive pointing to the generated file — for example part 'user.g.dart';. Without it, code generation output will not be found and your project will not compile.

IDs

Every root collection needs exactly one persisted id field named dbId with type Id.

Auto-Increment IDs

Use autoIncrement when Cindel should assign the ID on first write.
After calling put, the generated ID setter writes the allocated value back into the object:
This is the standard style for mutable model classes.

Explicit IDs

Use an explicit ID when your app owns the value — for example imported data, deterministic fixtures, or records whose ID is assigned by a server.

Persisted Field Types

Cindel persists every field whose type is in the supported set and that is not marked @ignore. Supported field shapes are:
Nullable fields work for any supported underlying type:
Nested lists are not supported. A List<List<int>> field will not be persisted. Use embedded objects for structured child values and a separate root collection for independently stored objects.

Ignoring Fields

Use @ignore (or @Ignore()) to exclude a field from persistence entirely. The field exists in Dart but Cindel will never read or write it.
Ignored fields are useful for UI-only labels, temporary selection state, derived values, caches, and anything that does not need to survive a database close and reopen. Do not mark real app data as ignored — if a value must survive a restart, model it as a supported persisted field.

Enums

Cindel persists enum fields when you annotate them with @Enumerated and choose a storage strategy.

Name-Based Storage

CindelEnumType.name stores the enum case name as a string. This is the easiest strategy to read and debug.
Treat enum case names as stored data once the app has persisted records — renaming a case is a breaking change.

Ordinal-Based Storage

CindelEnumType.ordinal stores the enum case index (0, 1, 2 …). It is compact, but enum declaration order becomes part of the stored contract.
Only use ordinal storage when you are confident the declaration order will never change.

Value-Based Storage

CindelEnumType.value stores the value of a named instance field. Use this when your enum cases carry durable external codes.

Indexes

Indexes speed up queries and let you enforce uniqueness constraints. Cindel provides single-field indexes and class-level composite indexes.

Single-Field Index

Place @Index (or the lowercase @index constant) directly on a field.
@Index options:

Index Types

Composite Index

Declare composite indexes at the class level using the indexes parameter of @Collection.
CompositeIndex takes a list of field names in the order they form the composite key. It supports the same unique, replace, and caseSensitive options as @Index.

Modeling Checklist

Before running code generation, verify each collection from your app’s point of view:
1

Correct annotation

The class has @Collection(name: ...) or @collection, and a part directive for the generated file.
2

One persisted ID

There is exactly one Id dbId field, set to autoIncrement or an explicit value as appropriate.
3

Supported field types only

Every persisted field uses a supported type. Runtime-only fields are marked @ignore.
4

Stable stored names

@Collection(name: ...) and @Name(...) values are stable if real user data already exists.
5

Enum strategies confirmed

Each @Enumerated field uses a strategy whose contract (case names, ordinal positions, or value fields) is stable.
6

Indexes match query patterns

Every field used in a sorted, range, or equality query has the appropriate index type.