Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mainser/cindel/llms.txt

Use this file to discover all available pages before exploring further.

Cindel gives Flutter apps a fully generated typed database API backed by MDBX on native platforms and SQLite/OPFS on the web. This guide walks through everything from adding the packages to your pubspec all the way to querying live data — you will have a working local database in your Flutter project in under five minutes.
Flutter Web requires a few additional setup steps for the Worker and Wasm assets provided by cindel_flutter_libs. See the Installation page for the complete platform-specific guide.
1

Add Dependencies

Open your project’s pubspec.yaml and add cindel and cindel_flutter_libs as runtime dependencies, then add build_runner and cindel_generator as dev dependencies.
dependencies:
  cindel: ^0.9.1
  cindel_flutter_libs: ^0.9.1

dev_dependencies:
  build_runner: ^2.15.0
  cindel_generator: ^0.9.1
cindel_flutter_libs carries no public Dart API. It is present so Flutter’s platform build can find and bundle the prebuilt native libraries on Android, iOS, macOS, Windows, and Linux, and the Worker/Wasm assets for Flutter Web. No import from this package is required in your application code.
2

Define a Model

Create a Dart file for your model. Add a part directive for the generated file, import package:cindel/cindel.dart, and annotate the class with @Collection. Every collection must declare exactly one Id field named dbId; set it to autoIncrement so Cindel assigns ids automatically.
import 'package:cindel/cindel.dart';

part 'user.g.dart';

@Collection(name: 'users')
class User {
  Id dbId = autoIncrement;

  @Index(unique: true)
  late String email;

  @Index()
  late String name;

  bool active = true;

  DateTime createdAt = DateTime.now().toUtc();
}
@Index(unique: true) on email tells the generator to produce a unique index and a putByEmail upsert helper. @Index() on name produces range-style query helpers. Fields without an annotation are still persisted — they just cannot be used in generated where() index lookups.
3

Run the Generator

Run build_runner from your project root to produce the user.g.dart part file. The generator emits the collection schema, serializers, typed collection accessor, where() index helpers, and filter() query helpers.
dart run build_runner build --delete-conflicting-outputs
During active development, use watch mode to regenerate automatically whenever you save a model file:
dart run build_runner watch --delete-conflicting-outputs
After the command completes, a user.g.dart file appears next to user.dart. Commit both files to version control.
4

Open the Database

Call Cindel.open with the path to a writable directory and the list of schemas produced by the generator. On native platforms, Cindel uses MDBX by default. Pass additional schemas to the list as your model count grows.
import 'package:path_provider/path_provider.dart';
import 'package:cindel/cindel.dart';

import 'models/user.dart';

Future<CindelDatabase> openDatabase() async {
  final directory = await getApplicationDocumentsDirectory();

  return Cindel.open(
    directory: directory.path,
    schemas: [UserSchema],
  );
}
Cindel.open is async and returns a CindelDatabase. Keep the returned handle alive for the lifetime of your app, and call db.close() only when you are fully done with the database (for example, in a test teardown or app shutdown hook).
5

Write and Read Data

With a CindelDatabase in hand, access the generated users collection directly from the database object. All collection methods are async.
// Create and persist a user.
final user = User()
  ..email = 'ada@example.com'
  ..name = 'Ada Lovelace';

await db.users.put(user);

// Fetch by auto-assigned id.
final saved = await db.users.get(user.dbId);

// Query with a generated filter helper.
final activeUsers = await db.users
    .filter()
    .activeEqualTo(true)
    .sortByName()
    .findAll();

// Use an indexed where() lookup for fast key access.
final ada = await db.users
    .where()
    .emailEqualTo('ada@example.com')
    .findFirst();

// Close when done (tests and shutdown only).
await db.close();
After put, Cindel writes the assigned auto-increment id back to user.dbId, so you can pass that id directly to get without an additional query.
Use Cindel.openInMemory in unit tests to get a fully functional in-memory database without touching the file system. Call db.close() in addTearDown so each test gets a clean state:
test('stores users', () async {
  final db = await Cindel.openInMemory(schemas: [UserSchema]);
  addTearDown(db.close);

  final user = User()
    ..email = 'ada@example.com'
    ..name = 'Ada';

  await db.users.put(user);

  expect(await db.users.get(user.dbId), isNotNull);
});

Build docs developers (and LLMs) love