目录
1.配置项
首先,让我们将Drift添加到你的项目中pubspec.yaml。除了核心Drift依赖项(drift以及drift_dev生成代码)之外,我们还添加了一个包,用于在相应的平台上打开数据库。
dependencies:
# 数据库
drift: ^2.26.0
drift_flutter: ^0.2.4
path_provider: ^2.1.5
path: ^1.9.0
dev_dependencies:
drift_dev: ^2.26.0
build_runner: ^2.4.15
2.数据库表类
每个使用 Drift 的项目都需要至少一个类来访问数据库。在本例中,我们假设这个数据库类定义在名为 的文件中database.dart,该文件位于 目录下的某个位置lib/。当然,您可以将此类放在任何您喜欢的 Dart 文件中。
这里我们要注意,提前在database.dart,相同目录添加空文件database.g.dart,
- 执行dart run build_runner build 一次生成所有必需的代码。
- dart run build_runner watch监视源代码中的更改,并通过增量重建生成代码。这适用于开发会话。
import 'package:drift/drift.dart';
part 'database.g.dart'; //执行 dart run build_runner build 会生成对应的操作当前表数据库代码
class TodoItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 6, max: 32)();
TextColumn get content => text().named('body')();
DateTimeColumn get createdAt => dateTime().nullable()();
}
(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
int get schemaVersion => 1;
static QueryExecutor _openConnection() {
return driftDatabase(
name: 'my_database',
native: const DriftNativeOptions(
// By default, `driftDatabase` from `package:drift_flutter` stores the
// database files in `getApplicationDocumentsDirectory()`.
databaseDirectory: getApplicationSupportDirectory,
),
// If you need web support, see https://drift.simonbinder.eu/platforms/web/
);
}
}
3.增删改查API使用
3.1 增
//1.创建一条新数据对象
await managers.todoItems
.create((o) => o(title: 'Title', content: 'Content'));
//2.创建新数据对象,并且可以执行创建方式
await managers.todoItems.create(
(o) => o(title: 'Title', content: 'New Content'),
mode: InsertMode.replace);
// 3.可以同时创建多个数据对象
await managers.todoItems.bulkCreate(
(o) => [
o(title: 'Title 1', content: 'Content 1'),
o(title: 'Title 2', content: 'Content 2'),
],
);
3.2 删
//1.删除所有数据
await managers.todoItems.delete();
// 2.根据id删除某一条数据
await managers.todoItems.filter((f) => f.id(5)).delete();
3.3 改
// 1.更新所有数据
await managers.todoItems
.update((o) => o(content: Value('New Content')));
// 2.根据条件更新部分数据
await managers.todoItems
.filter((f) => f.id.isIn([1, 2, 3]))
.update((o) => o(content: Value('New Content')));
3.4 查
// 1.获取所有数据
managers.todoItems.get().then((onValue) {
this.onValue = onValue;
setState(() {});
});
// 2.当数据流有所改变,则会更新
managers.todoItems.watch();
// 3.根据匹配ID获取某一个数据对象
await managers.todoItems.filter((f) => f.id(1)).getSingle();
4.Demo列子
4.1 DriftPage界面代码
import 'package:drift/drift.dart' as drift;
import 'package:flutter/material.dart';
import 'package:flutter_demo/drift/database.dart';
class DriftPage extends StatefulWidget {
const DriftPage({Key? key}) : super(key: key);
_DriftPageState createState() => _DriftPageState();
}
class _DriftPageState extends State<DriftPage> {
late AppDatabase appDatabase;
String content = "";
late $AppDatabaseManager managers;
List<TodoItem> onValue = [];
void initState() {
super.initState();
appDatabase = AppDatabase();
managers = appDatabase.managers;
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("drift"),
),
body: SingleChildScrollView(
child: Container(
child: Column(
children: [
ElevatedButton(
onPressed: () async {
createTodoItem();
},
child: const Text('插入 insert'),
),
ElevatedButton(
onPressed: () async {
selectTodoItems();
},
child: const Text('查询 select'),
),
ElevatedButton(
onPressed: () async {
updateTodoItems();
setState(() {});
},
child: const Text('更新 update'),
),
ElevatedButton(
onPressed: () async {
deleteTodoItems();
},
child: const Text('删除 delete'),
),
listWidget(onValue)
],
),
),
),
);
}
listWidget(List<TodoItem> onValue) {
return Column(
children: onValue
.map((item) => Column(
children: [
Text(item.id.toString()),
Text(item.title),
Text(item.content),
],
))
.toList(),
);
}
Future<void> selectTodoItems() async {
// Get all items
managers.todoItems.get().then((onValue) {
this.onValue = onValue;
setState(() {});
});
// A stream of all the todo items, updated in real-time
managers.todoItems.watch();
// To get a single item, apply a filter and call `getSingle`
await managers.todoItems.filter((f) => f.id(1)).getSingle();
}
Future<void> updateTodoItems() async {
// 更新所有数据
await managers.todoItems
.update((o) => o(content: drift.Value('New Content')));
// 根据条件更新部分数据
await managers.todoItems
.filter((f) => f.id.isIn([1, 2, 3]))
.update((o) => o(content: drift.Value('New Content')));
}
Future<void> replaceTodoItems() async {
// Replace a single item
var obj = await managers.todoItems.filter((o) => o.id(1)).getSingle();
obj = obj.copyWith(content: 'New Content');
await managers.todoItems.replace(obj);
// Replace multiple items
var objs =
await managers.todoItems.filter((o) => o.id.isIn([1, 2, 3])).get();
objs = objs.map((o) => o.copyWith(content: 'New Content')).toList();
await managers.todoItems.bulkReplace(objs);
}
Future<void> createTodoItem() async {
// Create a new item
await managers.todoItems
.create((o) => o(title: 'Title', content: 'Content'));
// We can also use `mode` and `onConflict` parameters, just
// like in the `[InsertStatement.insert]` method on the table
await managers.todoItems.create(
(o) => o(title: 'Title', content: 'New Content'),
mode: drift.InsertMode.replace);
// We can also create multiple items at once
await managers.todoItems.bulkCreate(
(o) => [
o(title: 'Title 1', content: 'Content 1'),
o(title: 'Title 2', content: 'Content 2'),
],
);
setState(() {});
}
Future<void> deleteTodoItems() async {
// Delete all items
await managers.todoItems.delete();
setState(() {
this.onValue = [];
});
// Delete a single item
await managers.todoItems.filter((f) => f.id(5)).delete();
}
}
4.2 代码database.g.dart,可根据2步骤自动生成
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'database.dart';
// ignore_for_file: type=lint
class $TodoItemsTable extends TodoItems
with TableInfo<$TodoItemsTable, TodoItem> {
final GeneratedDatabase attachedDatabase;
final String? _alias;
$TodoItemsTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints:
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
static const VerificationMeta _titleMeta = const VerificationMeta('title');
late final GeneratedColumn<String> title = GeneratedColumn<String>(
'title', aliasedName, false,
additionalChecks:
GeneratedColumn.checkTextLength(minTextLength: 0, maxTextLength: 32),
type: DriftSqlType.string,
requiredDuringInsert: true);
static const VerificationMeta _contentMeta =
const VerificationMeta('content');
late final GeneratedColumn<String> content = GeneratedColumn<String>(
'body', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt');
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, true,
type: DriftSqlType.dateTime, requiredDuringInsert: false);
List<GeneratedColumn> get $columns => [id, title, content, createdAt];
String get aliasedName => _alias ?? actualTableName;
String get actualTableName => $name;
static const String $name = 'todo_items';
VerificationContext validateIntegrity(Insertable<TodoItem> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('title')) {
context.handle(
_titleMeta, title.isAcceptableOrUnknown(data['title']!, _titleMeta));
} else if (isInserting) {
context.missing(_titleMeta);
}
if (data.containsKey('body')) {
context.handle(_contentMeta,
content.isAcceptableOrUnknown(data['body']!, _contentMeta));
} else if (isInserting) {
context.missing(_contentMeta);
}
if (data.containsKey('created_at')) {
context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
}
return context;
}
Set<GeneratedColumn> get $primaryKey => {id};
TodoItem map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return TodoItem(
id: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
title: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}title'])!,
content: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}body'])!,
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at']),
);
}
$TodoItemsTable createAlias(String alias) {
return $TodoItemsTable(attachedDatabase, alias);
}
}
class TodoItem extends DataClass implements Insertable<TodoItem> {
final int id;
final String title;
final String content;
final DateTime? createdAt;
const TodoItem(
{required this.id,
required this.title,
required this.content,
this.createdAt});
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['title'] = Variable<String>(title);
map['body'] = Variable<String>(content);
if (!nullToAbsent || createdAt != null) {
map['created_at'] = Variable<DateTime>(createdAt);
}
return map;
}
TodoItemsCompanion toCompanion(bool nullToAbsent) {
return TodoItemsCompanion(
id: Value(id),
title: Value(title),
content: Value(content),
createdAt: createdAt == null && nullToAbsent
? const Value.absent()
: Value(createdAt),
);
}
factory TodoItem.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return TodoItem(
id: serializer.fromJson<int>(json['id']),
title: serializer.fromJson<String>(json['title']),
content: serializer.fromJson<String>(json['content']),
createdAt: serializer.fromJson<DateTime?>(json['createdAt']),
);
}
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'title': serializer.toJson<String>(title),
'content': serializer.toJson<String>(content),
'createdAt': serializer.toJson<DateTime?>(createdAt),
};
}
TodoItem copyWith(
{int? id,
String? title,
String? content,
Value<DateTime?> createdAt = const Value.absent()}) =>
TodoItem(
id: id ?? this.id,
title: title ?? this.title,
content: content ?? this.content,
createdAt: createdAt.present ? createdAt.value : this.createdAt,
);
TodoItem copyWithCompanion(TodoItemsCompanion data) {
return TodoItem(
id: data.id.present ? data.id.value : this.id,
title: data.title.present ? data.title.value : this.title,
content: data.content.present ? data.content.value : this.content,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
);
}
String toString() {
return (StringBuffer('TodoItem(')
..write('id: $id, ')
..write('title: $title, ')
..write('content: $content, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
int get hashCode => Object.hash(id, title, content, createdAt);
bool operator ==(Object other) =>
identical(this, other) ||
(other is TodoItem &&
other.id == this.id &&
other.title == this.title &&
other.content == this.content &&
other.createdAt == this.createdAt);
}
class TodoItemsCompanion extends UpdateCompanion<TodoItem> {
final Value<int> id;
final Value<String> title;
final Value<String> content;
final Value<DateTime?> createdAt;
const TodoItemsCompanion({
this.id = const Value.absent(),
this.title = const Value.absent(),
this.content = const Value.absent(),
this.createdAt = const Value.absent(),
});
TodoItemsCompanion.insert({
this.id = const Value.absent(),
required String title,
required String content,
this.createdAt = const Value.absent(),
}) : title = Value(title),
content = Value(content);
static Insertable<TodoItem> custom({
Expression<int>? id,
Expression<String>? title,
Expression<String>? content,
Expression<DateTime>? createdAt,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (title != null) 'title': title,
if (content != null) 'body': content,
if (createdAt != null) 'created_at': createdAt,
});
}
TodoItemsCompanion copyWith(
{Value<int>? id,
Value<String>? title,
Value<String>? content,
Value<DateTime?>? createdAt}) {
return TodoItemsCompanion(
id: id ?? this.id,
title: title ?? this.title,
content: content ?? this.content,
createdAt: createdAt ?? this.createdAt,
);
}
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (title.present) {
map['title'] = Variable<String>(title.value);
}
if (content.present) {
map['body'] = Variable<String>(content.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
return map;
}
String toString() {
return (StringBuffer('TodoItemsCompanion(')
..write('id: $id, ')
..write('title: $title, ')
..write('content: $content, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
}
abstract class _$AppDatabase extends GeneratedDatabase {
_$AppDatabase(QueryExecutor e) : super(e);
$AppDatabaseManager get managers => $AppDatabaseManager(this);
late final $TodoItemsTable todoItems = $TodoItemsTable(this);
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
List<DatabaseSchemaEntity> get allSchemaEntities => [todoItems];
}
typedef $$TodoItemsTableCreateCompanionBuilder = TodoItemsCompanion Function({
Value<int> id,
required String title,
required String content,
Value<DateTime?> createdAt,
});
typedef $$TodoItemsTableUpdateCompanionBuilder = TodoItemsCompanion Function({
Value<int> id,
Value<String> title,
Value<String> content,
Value<DateTime?> createdAt,
});
class $$TodoItemsTableFilterComposer
extends Composer<_$AppDatabase, $TodoItemsTable> {
$$TodoItemsTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnFilters<int> get id => $composableBuilder(
column: $table.id, builder: (column) => ColumnFilters(column));
ColumnFilters<String> get title => $composableBuilder(
column: $table.title, builder: (column) => ColumnFilters(column));
ColumnFilters<String> get content => $composableBuilder(
column: $table.content, builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnFilters(column));
}
class $$TodoItemsTableOrderingComposer
extends Composer<_$AppDatabase, $TodoItemsTable> {
$$TodoItemsTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnOrderings<int> get id => $composableBuilder(
column: $table.id, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get title => $composableBuilder(
column: $table.title, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get content => $composableBuilder(
column: $table.content, builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnOrderings(column));
}
class $$TodoItemsTableAnnotationComposer
extends Composer<_$AppDatabase, $TodoItemsTable> {
$$TodoItemsTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
GeneratedColumn<int> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
GeneratedColumn<String> get title =>
$composableBuilder(column: $table.title, builder: (column) => column);
GeneratedColumn<String> get content =>
$composableBuilder(column: $table.content, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
}
class $$TodoItemsTableTableManager extends RootTableManager<
_$AppDatabase,
$TodoItemsTable,
TodoItem,
$$TodoItemsTableFilterComposer,
$$TodoItemsTableOrderingComposer,
$$TodoItemsTableAnnotationComposer,
$$TodoItemsTableCreateCompanionBuilder,
$$TodoItemsTableUpdateCompanionBuilder,
(TodoItem, BaseReferences<_$AppDatabase, $TodoItemsTable, TodoItem>),
TodoItem,
PrefetchHooks Function()> {
$$TodoItemsTableTableManager(_$AppDatabase db, $TodoItemsTable table)
: super(TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
$$TodoItemsTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
$$TodoItemsTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
$$TodoItemsTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback: ({
Value<int> id = const Value.absent(),
Value<String> title = const Value.absent(),
Value<String> content = const Value.absent(),
Value<DateTime?> createdAt = const Value.absent(),
}) =>
TodoItemsCompanion(
id: id,
title: title,
content: content,
createdAt: createdAt,
),
createCompanionCallback: ({
Value<int> id = const Value.absent(),
required String title,
required String content,
Value<DateTime?> createdAt = const Value.absent(),
}) =>
TodoItemsCompanion.insert(
id: id,
title: title,
content: content,
createdAt: createdAt,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
));
}
typedef $$TodoItemsTableProcessedTableManager = ProcessedTableManager<
_$AppDatabase,
$TodoItemsTable,
TodoItem,
$$TodoItemsTableFilterComposer,
$$TodoItemsTableOrderingComposer,
$$TodoItemsTableAnnotationComposer,
$$TodoItemsTableCreateCompanionBuilder,
$$TodoItemsTableUpdateCompanionBuilder,
(TodoItem, BaseReferences<_$AppDatabase, $TodoItemsTable, TodoItem>),
TodoItem,
PrefetchHooks Function()>;
class $AppDatabaseManager {
final _$AppDatabase _db;
$AppDatabaseManager(this._db);
$$TodoItemsTableTableManager get todoItems =>
$$TodoItemsTableTableManager(_db, _db.todoItems);
}
5.常见问题
默认使用方法
late AppDatabase database;
void main() {
database = AppDatabase();
runApp(MyFlutterApp());
}
provider
如果您正在使用provider程序包,则可以将顶级小部件包装在管理数据库实例的提供程序中:
void main() {
runApp(
Provider<AppDatabase>(
create: (context) => AppDatabase(),
child: MyFlutterApp(),
dispose: (context, db) => db.close(),
),
);
}
GetX 则可以将其添加为管理数据库实例的服务
void main() {
var put = Get.put(AppDatabase());
Get.find<AppDatabase>();
runApp(MyFlutterApp());
}
然后,您的小部件就可以使用 访问数据库了Get.find().your_method