5.3 KiB
| title | description |
|---|---|
| Insert, Update, Delete | Implement Drift writes, companions, transactions, and batches |
Scope Rule
From Flutter code outside the database class, call write builders on the database instance:
await database.into(database.todoItems).insert(
TodoItemsCompanion.insert(title: title),
);
Use bare into(todoItems), update(todoItems), and delete(todoItems) only inside GeneratedDatabase or DatabaseAccessor code.
Insert
Insert a row with required values:
final id = await database.into(database.todoItems).insert(
TodoItemsCompanion.insert(
title: 'First todo',
content: const Value('Some description'),
),
);
Omit nullable columns and columns with SQL defaults:
final id = await database.into(database.todoItems).insert(
TodoItemsCompanion.insert(title: 'First todo'),
);
Insert multiple rows in a batch:
await database.batch((batch) {
batch.insertAll(database.todoItems, [
TodoItemsCompanion.insert(title: 'First entry'),
TodoItemsCompanion.insert(
title: 'Another entry',
priority: const Value(2),
),
]);
});
Upsert
Use insertOnConflictUpdate when the row has a primary key or unique key that should be replaced on conflict.
await database.into(database.todoItems).insertOnConflictUpdate(
TodoItemsCompanion.insert(
id: const Value(1),
title: 'Updated title',
),
);
For custom conflict handling, pass a DoUpdate clause with an update companion:
await database.into(database.todoItems).insert(
TodoItemsCompanion.insert(
id: const Value(1),
title: 'Important',
),
onConflict: DoUpdate(
(old) => const TodoItemsCompanion(
priority: Value(5),
),
),
);
Use insertReturning only when the target SQLite runtime supports RETURNING.
final row = await database.into(database.todoItems).insertReturning(
TodoItemsCompanion.insert(title: 'A todo entry'),
);
Update
Always add a where clause unless the intent is truly to update every row.
await (database.update(database.todoItems)
..where((t) => t.id.equals(id)))
.write(
const TodoItemsCompanion(
title: Value('Updated title'),
),
);
Update multiple columns with a companion:
await (database.update(database.todoItems)
..where((t) => t.id.equals(id)))
.write(
const TodoItemsCompanion(
title: Value('Updated title'),
isCompleted: Value(true),
),
);
Use replace only when you have a complete generated data row:
await database.update(database.todoItems).replace(
todo.copyWith(isCompleted: true),
);
Use custom expressions for SQL-side updates:
await (database.update(database.todoItems)
..where((t) => t.id.equals(id)))
.write(
TodoItemsCompanion.custom(
priority: database.todoItems.priority + const Constant(1),
),
);
Delete
Delete matching rows with where and go():
await (database.delete(database.todoItems)
..where((t) => t.id.equals(id)))
.go();
Delete multiple rows:
await (database.delete(database.todoItems)
..where((t) => t.isCompleted.equals(true)))
.go();
Delete all rows only when that is explicitly intended:
await database.delete(database.todoItems).go();
Do not use delete(table).go(id). That is not Drift's delete API.
Companions And Value
Generated data classes represent full rows. Companions represent partial writes.
final companion = TodoItemsCompanion(
title: const Value('Title'),
content: const Value('Content'),
priority: const Value(2),
category: const Value.absent(),
);
For nullable fields, distinguish setting NULL from leaving a column unchanged:
const TodoItemsCompanion(
category: Value<int?>(null),
);
const TodoItemsCompanion(
category: Value.absent(),
);
Transactions
Use transactions for multi-step writes that must succeed or fail together.
final categoryId = await database.transaction(() async {
final id = await database.into(database.categories).insert(
CategoriesCompanion.insert(name: 'Work'),
);
await database.into(database.todoItems).insert(
TodoItemsCompanion.insert(
title: 'First work todo',
category: Value(id),
),
);
return id;
});
Errors thrown inside the callback roll the transaction back.
Batch Operations
Batches are useful for many similar writes.
await database.batch((batch) {
batch.insertAll(database.todoItems, [
TodoItemsCompanion.insert(title: 'First'),
TodoItemsCompanion.insert(title: 'Second'),
]);
batch.update(
database.todoItems,
const TodoItemsCompanion(priority: Value(1)),
where: (t) => t.isCompleted.equals(false),
);
batch.deleteWhere(
database.todoItems,
(t) => t.isCompleted.equals(true),
);
});
Use batch.delete(table, row) only when you have an insertable row with the primary key. Use batch.deleteWhere for predicates.
Write Safety
Avoid broad updates and deletes in UI actions:
// Bad: updates every todo.
await database.update(database.todoItems).write(
const TodoItemsCompanion(isCompleted: Value(true)),
);
// Good: updates one row.
await (database.update(database.todoItems)
..where((t) => t.id.equals(id)))
.write(
const TodoItemsCompanion(isCompleted: Value(true)),
);