skills/flutter-drift/references/tables.md

234 lines
5.5 KiB
Markdown

---
title: Table Definitions
description: Define Drift tables, columns, keys, indexes, and constraints
---
## Table Style
Use generated Dart table classes for ordinary Flutter apps. Keep the schema close to the domain model, but remember that Drift data classes are generated from the table definitions.
```dart
@DataClassName('TodoCategory')
class Categories extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text().withLength(min: 1, max: 80)();
}
class TodoItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 1, max: 120)();
TextColumn get content => text().nullable()();
BoolColumn get isCompleted =>
boolean().withDefault(const Constant(false))();
IntColumn get priority => integer().withDefault(const Constant(0))();
IntColumn get category =>
integer().nullable().references(Categories, #id)();
DateTimeColumn get createdAt =>
dateTime().withDefault(currentDateAndTime)();
}
```
Add every table to the database annotation:
```dart
@DriftDatabase(tables: [Categories, TodoItems])
class AppDatabase extends _$AppDatabase {
AppDatabase(QueryExecutor executor) : super(executor);
@override
int get schemaVersion => 1;
}
```
## Column Types
| Dart value | Drift column builder | SQLite storage |
| --- | --- | --- |
| `int` | `integer()` | `INTEGER` |
| `BigInt` | `int64()` | `INTEGER` |
| `String` | `text()` | `TEXT` |
| `bool` | `boolean()` | `INTEGER` |
| `double` | `real()` | `REAL` |
| `Uint8List` | `blob()` | `BLOB` |
| `DateTime` | `dateTime()` | `INTEGER` or `TEXT` |
Use `nullable()` for optional values:
```dart
TextColumn get content => text().nullable()();
```
Use SQL defaults for values that should exist even when inserts omit them:
```dart
BoolColumn get isCompleted =>
boolean().withDefault(const Constant(false))();
DateTimeColumn get createdAt =>
dateTime().withDefault(currentDateAndTime)();
```
Use client defaults only when the value must be computed in Dart:
```dart
TextColumn get clientId => text().clientDefault(() => const Uuid().v4())();
```
## Keys And References
Use `autoIncrement()` for ordinary integer primary keys:
```dart
IntColumn get id => integer().autoIncrement()();
```
Use a custom primary key only when the domain really owns the identifier:
```dart
class Profiles extends Table {
TextColumn get email => text()();
@override
Set<Column<Object>> get primaryKey => {email};
}
```
Reference another table with `references`:
```dart
class TodoItems extends Table {
IntColumn get category =>
integer().nullable().references(Categories, #id)();
}
```
Enable SQLite foreign keys when the app relies on enforcement:
```dart
@override
MigrationStrategy get migration {
return MigrationStrategy(
beforeOpen: (details) async {
await customStatement('PRAGMA foreign_keys = ON');
},
);
}
```
## Unique Constraints
Use `unique()` for a single column:
```dart
TextColumn get username => text().unique()();
```
Use `uniqueKeys` for multi-column uniqueness:
```dart
class Reservations extends Table {
TextColumn get room => text()();
DateTimeColumn get onDay => dateTime()();
@override
List<Set<Column<Object>>> get uniqueKeys => [
{room, onDay},
];
}
```
## Indexes
Index columns that are frequently filtered, sorted, or joined.
```dart
@TableIndex(name: 'todo_items_completed', columns: {#isCompleted})
@TableIndex(
name: 'todo_items_created_at',
columns: {IndexedColumn(#createdAt, orderBy: OrderingMode.desc)},
)
class TodoItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text()();
BoolColumn get isCompleted =>
boolean().withDefault(const Constant(false))();
DateTimeColumn get createdAt =>
dateTime().withDefault(currentDateAndTime)();
}
```
Use custom SQL indexes for partial indexes:
```dart
@TableIndex.sql('''
CREATE INDEX pending_todos ON todo_items (created_at)
WHERE is_completed = 0;
''')
class TodoItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text()();
BoolColumn get isCompleted =>
boolean().withDefault(const Constant(false))();
DateTimeColumn get createdAt =>
dateTime().withDefault(currentDateAndTime)();
}
```
## Constraints
Use length constraints for user-facing text:
```dart
TextColumn get title => text().withLength(min: 1, max: 120)();
```
Use check constraints for numeric domain rules:
```dart
IntColumn get priority => integer().check(
priority.isBiggerOrEqualValue(0) & priority.isSmallerOrEqualValue(5),
)();
```
Use generated columns when SQLite should compute derived data:
```dart
class Boxes extends Table {
IntColumn get length => integer()();
IntColumn get width => integer()();
IntColumn get area => integer().generatedAs(length * width)();
}
```
## Table Mixins
Use mixins for repeated columns, but keep them simple.
```dart
mixin TimestampColumns on Table {
DateTimeColumn get createdAt =>
dateTime().withDefault(currentDateAndTime)();
}
class Notes extends Table with TimestampColumns {
IntColumn get id => integer().autoIncrement()();
TextColumn get body => text()();
}
```
## Strict Tables
Use strict tables only when the target SQLite runtime supports them and the app benefits from stricter typing.
```dart
class Preferences extends Table {
TextColumn get key => text()();
AnyColumn get value => sqliteAny().nullable()();
@override
Set<Column<Object>> get primaryKey => {key};
@override
bool get isStrict => true;
}
```