> ## Documentation Index
> Fetch the complete documentation index at: https://cindel.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Freezed Models as Cindel Collections: Setup and Limits

> Cindel supports Freezed classic class and single primary factory styles. Learn the supported patterns and limitations before adopting Freezed with Cindel.

Freezed is a Dart code-generation package that produces immutable value classes with `copyWith`, structural equality, and `toString` for free. If your app already uses Freezed for domain models, Cindel can persist those models directly — as long as you stay within the two supported shapes. This page explains which Freezed patterns work with Cindel, how to set them up, and what to avoid.

## Supported Shapes

Cindel supports exactly two Freezed shapes for collections:

<CardGroup cols={2}>
  <Card title="Classic class" icon="cube">
    A `@freezed` class with a concrete constructor and `@override final` fields in the class body.
  </Card>

  <Card title="Single primary factory" icon="wand-magic-sparkles">
    A `@freezed` abstract class whose entire shape is defined by one `const factory` constructor.
  </Card>
</CardGroup>

<Warning>
  Freezed **union** and **sealed** models — classes that declare multiple named factory constructors to represent different states — are **not** supported as Cindel collections. If you need sealed modeling alongside persistence, keep your Freezed union types separate from your Cindel collection classes.
</Warning>

***

## Primary Factory Style

In the primary factory style, you place Cindel field annotations (`@Index`, `@Enumerated`, `@ignore`, and so on) directly on the factory constructor parameters.

```dart theme={null}
import 'package:cindel/cindel.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'product.freezed.dart';
part 'product.g.dart';

@freezed
@Collection(name: 'products')
abstract class Product with _$Product {
  const factory Product({
    required Id dbId,
    @Index(unique: true) required String sku,
    @Index() required String name,
    @Default(true) bool active,
    @ignore String? runtimeLabel,
  }) = _Product;
}
```

You can combine Cindel annotations with Freezed annotations on the same parameter:

```dart theme={null}
enum ProductState { draft, published }

@freezed
@Collection(name: 'products')
abstract class Product with _$Product {
  const factory Product({
    required Id dbId,
    required String sku,
    @Enumerated(CindelEnumType.name) required ProductState state,
    @ignore String? runtimeLabel,
  }) = _Product;
}
```

### IDs in the Primary Factory Style

Freezed primary factory models are immutable — Cindel cannot write an auto-assigned ID back into the object after `put`. For this reason, `dbId` must be `required` in the factory and you supply its value explicitly when constructing the object.

```dart theme={null}
// Supply the ID your app already owns — a server-assigned int, a hash, etc.
final product = const Product(
  dbId: 1001,
  sku: 'DART-001',
  name: 'Dart SDK',
  active: true,
);
await db.products.put(product);
```

If you want Cindel to manage IDs automatically, use a regular mutable class or the classic Freezed class style instead — those styles have an assignable `dbId` field that works with `autoIncrement`.

***

## Classic Class Style

In the classic class style, the Freezed model uses a concrete constructor and declares fields in the class body with `@override final`. Cindel annotations go on the field declarations, exactly as in a regular Cindel collection class.

```dart theme={null}
import 'package:cindel/cindel.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
@Collection(name: 'users')
class User with _$User {
  const User({
    required this.dbId,
    required this.email,
    required this.name,
  });

  @override
  final Id dbId;

  @override
  @Index(unique: true)
  final String email;

  @override
  final String name;
}
```

Use this style when you want explicit field declarations visible in the class body and prefer concrete classes over abstract ones.

***

## Required `part` Directives

Freezed collections need **two** `part` directives: one for Freezed output and one for Cindel output.

```dart theme={null}
part 'user.freezed.dart'; // generated by Freezed
part 'user.g.dart';        // generated by Cindel
```

Run both build steps together:

```bash theme={null}
dart run build_runner build
```

***

## Dev Dependencies

Add both `freezed` and `freezed_annotation` to your project alongside the Cindel generator.

```yaml theme={null}
dependencies:
  freezed_annotation: ^latest
  cindel: ^latest

dev_dependencies:
  freezed: ^latest
  build_runner: ^latest
  cindel_generator: ^latest
```

***

## Rules That Still Apply

A Freezed collection follows every rule that applies to a regular Cindel collection:

<Steps>
  <Step title="One persisted ID">
    The model must have exactly one `Id dbId` field. Classic class style supports `autoIncrement`; primary factory style requires an explicit value at construction time.
  </Step>

  <Step title="Supported field types">
    All persisted fields must use types Cindel supports — primitives, `DateTime`, `Duration`, enums with `@Enumerated`, embedded objects, and flat lists of those types.
  </Step>

  <Step title="Stable stored names">
    `@Collection(name: ...)` and `@Name(...)` values must remain stable once real data exists.
  </Step>

  <Step title="Single constructor shape">
    The Freezed model must have exactly one constructor shape. Do not add named union factories.
  </Step>
</Steps>

<Note>
  Embedded objects can also be Freezed models, using either supported style. Apply `@embedded` (or `@Embedded()`) in place of `@Collection` and omit the `dbId` field. All other Freezed and embedded constraints still apply.
</Note>
