跳转至

drift 数据库使用教程

drift 是 Flutter 官方推荐的 SQLite ORM,功能最强大、最稳定,适合大型应用。


一、drift 简介

1.1 什么是 drift?

drift(原名 moor)是 Flutter 最强大的 SQLite ORM: - 类型安全:编译时检查 SQL 错误 - 声明式:用 Dart 代码定义表结构 - 响应式:支持流式查询更新 UI - 全平台:支持 Android/iOS/Windows/macOS/Linux/Web

1.2 官方资源

资源 链接
Pub 页面 https://pub.dev/packages/drift
官方文档 https://drift.simonbinder.eu/
GitHub https://github.com/simonb97/drift
示例项目 https://github.com/simonb97/drift-examples

1.3 drift vs 其他方案

特性 drift floor sqflite
类型安全 ✅ 完整 ✅ 完整
代码生成 ✅ 自动 ✅ 自动
流式查询
Web 支持
迁移支持 ✅ 完整
学习曲线 中高

二、安装配置

2.1 添加依赖

# pubspec.yaml
dependencies:
  drift: ^2.14.0
  sqlite3_flutter_libs: ^0.5.18
  path_provider: ^2.1.1
  path: ^1.8.3

dev_dependencies:
  drift_dev: ^2.14.0
  build_runner: ^2.4.7

2.2 桌面平台额外配置

# 如果需要 Windows/macOS/Linux 支持
dependencies:
  drift: ^2.14.0
  sqlite3_flutter_libs: ^0.5.18
  drift_flutter: ^0.0.1  # 简化桌面集成

dev_dependencies:
  drift_dev: ^2.14.0
  build_runner: ^2.4.7

2.3 安装命令

flutter pub get

三、创建数据库

3.1 定义表(Models)

// lib/database/tables/users.dart
import 'package:drift/drift.dart';

class Users extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get name => text().withLength(min: 1, max: 100)();
  TextColumn get email => text().unique()();
  IntColumn get age => integer().nullable()();
  DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}

3.2 定义数据库类

// lib/database/database.dart
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'tables/users.dart';

part 'database.g.dart';

@DriftDatabase(tables: [Users])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(_openConnection());

  @override
  int get schemaVersion => 1;

  // 查询所有用户
  Future<List<User>> getAllUsers() => select(users).get();

  // 流式查询(实时更新)
  Stream<List<User>> watchAllUsers() => select(users).watch();

  // 插入用户
  Future<int> insertUser(UsersCompanion user) =>
      into(users).insert(user);

  // 更新用户
  Future<bool> updateUser(User user) => update(users).replace(user);

  // 删除用户
  Future<int> deleteUser(int id) =>
      (delete(users)..where((t) => t.id.equals(id))).go();
}

LazyDatabase _openConnection() {
  return driftDatabase(name: 'my_database');
}

3.3 生成代码

dart run build_runner build

生成后会产生 database.g.dart 文件。


四、进阶查询

4.1 条件查询

// 查询单个用户
Future<User?> getUserById(int id) =>
    (select(users)..where((t) => t.id.equals(id))).getSingleOrNull();

// 模糊查询
Future<List<User>> searchUsers(String keyword) =>
    (select(users)..where((t) => t.name.like('%$keyword%'))).get();

// 多条件查询
Future<List<User>> getAdultUsers() =>
    (select(users)
      ..where((t) => t.age.isNotNull() & t.age.isBiggerOrEqualValue(18))
      ..orderBy([(t) => OrderingTerm.desc(t.createdAt)]))
    .get();

4.2 分页查询

Future<List<User>> getUsersPaginated(int page, int pageSize) {
  final offset = page * pageSize;
  return (select(users)
    ..limit(pageSize, offset: offset)
    ..orderBy([(t) => OrderingTerm.desc(t.createdAt)]))
    .get();
}

4.3 聚合查询

// 统计用户数量
Future<int> getUserCount() async {
  final count = users.id.count();
  final query = selectOnly(users)..addColumns([count]);
  final result = await query.getSingle();
  return result.read(count) ?? 0;
}

// 统计平均年龄
Future<double?> getAverageAge() async {
  final avg = users.age.avg();
  final query = selectOnly(users)..addColumns([avg]);
  final result = await query.getSingle();
  return result.read(avg);
}

五、数据迁移

5.1 添加新表

@DriftDatabase(tables: [Users, Posts])
class AppDatabase extends _$AppDatabase {
  @override
  int get schemaVersion => 2;  // 版本号+1

  @override
  MigrationStrategy get migration {
    return MigrationStrategy(
      onCreate: (Migrator m) async {
        await m.createAll();
      },
      onUpgrade: (Migrator m, int from, int to) async {
        // 处理迁移
        if (from < 2) {
          // 从 v1 升级到 v2
          await m.createTable(posts);
        }
      },
    );
  }
}

5.2 添加/修改列

// 添加新列
onUpgrade: (Migrator m, int from, int to) async {
  if (from < 3) {
    await m.addColumn(users, users.phoneNumber);
  }
},

5.3 数据迁移示例

@override
MigrationStrategy get migration {
  return MigrationStrategy(
    onCreate: (Migrator m) async {
      await m.createAll();
    },
    onUpgrade: (Migrator m, int from, int to) async {
      if (from == 1) {
        // v1 -> v2
        await m.addColumn(users, users.avatar);
      }
      if (from < 3) {
        // 所有小于 v3 的版本都需要这个迁移
        await m.addColumn(users, users.bio);
      }
    },
    beforeOpen: (details) async {
      // 每次打开数据库时执行
      if (details.wasCreated) {
        // 数据库刚创建,插入初始数据
        await _insertDefaultData();
      }
    },
  );
}

六、结合 GetX 使用

6.1 创建 Service

// lib/services/database_service.dart
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import '../database/database.dart';

class DatabaseService extends GetxService {
  late final AppDatabase _db;

  AppDatabase get db => _db;

  Future<DatabaseService> init() async {
    final dir = await getApplicationDocumentsDirectory();
    _db = AppDatabase(LazyDatabase(() => SqliteDatabase.file(
      File(p.join(dir.path, 'my_database.sqlite')),
    )));
    return this;
  }

  @override
  void onClose() {
    _db.close();
    super.onClose();
  }
}

6.2 初始化数据库

// main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化数据库
  await Get.putAsync(() => DatabaseService().init());

  runApp(MyApp());
}

6.3 创建 GetxController

// lib/controllers/user_controller.dart
import 'package:get/get.dart';
import '../database/database.dart';
import '../services/database_service.dart';

class UserController extends GetxController {
  final DatabaseService _dbService = Get.find<DatabaseService>();

  final RxList<User> users = <User>[].obs;
  final RxBool isLoading = false.obs;

  @override
  void onInit() {
    super.onInit();
    _watchUsers();
  }

  void _watchUsers() {
    _dbService.db.watchAllUsers().listen((userList) {
      users.value = userList;
    });
  }

  Future<void> addUser(String name, String email, int? age) async {
    await _dbService.db.insertUser(UsersCompanion(
      name: Value(name),
      email: Value(email),
      age: Value(age),
    ));
  }

  Future<void> deleteUser(int id) async {
    await _dbService.db.deleteUser(id);
  }
}

6.4 页面使用

class UserListPage extends StatelessWidget {
  final UserController ctrl = Get.put(UserController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('用户列表')),
      body: Obx(() {
        if (ctrl.isLoading.value) {
          return Center(child: CircularProgressIndicator());
        }
        return ListView.builder(
          itemCount: ctrl.users.length,
          itemBuilder: (context, index) {
            final user = ctrl.users[index];
            return ListTile(
              title: Text(user.name),
              subtitle: Text(user.email),
              trailing: IconButton(
                icon: Icon(Icons.delete),
                onPressed: () => ctrl.deleteUser(user.id),
              ),
            );
          },
        );
      }),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showAddDialog(),
        child: Icon(Icons.add),
      ),
    );
  }

  void _showAddDialog() {
    Get.dialog(
      AlertDialog(
        title: Text('添加用户'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              decoration: InputDecoration(labelText: '姓名'),
              onChanged: (v) => name = v,
            ),
            TextField(
              decoration: InputDecoration(labelText: '邮箱'),
              onChanged: (v) => email = v,
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Get.back(),
            child: Text('取消'),
          ),
          ElevatedButton(
            onPressed: () {
              ctrl.addUser(name, email, null);
              Get.back();
            },
            child: Text('添加'),
          ),
        ],
      ),
    );
  }
}

七、实战示例:聊天消息存储

// 定义消息表
class Messages extends Table {
  IntColumn get id => integer().autoIncrement()();
  IntColumn get conversationId => integer()();
  TextColumn get content => text()();
  IntColumn get senderId => integer()();
  BoolColumn get isRead => boolean().withDefault(const Constant(false))();
  DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}

@DriftDatabase(tables: [Messages])
class AppDatabase extends _$AppDatabase {
  // 查询会话消息
  Stream<List<Message>> watchConversationMessages(int convId) {
    return (select(messages)
      ..where((t) => t.conversationId.equals(convId))
      ..orderBy([(t) => OrderingTerm.desc(t.createdAt)]))
      .watch();
  }

  // 获取未读消息数
  Future<int> getUnreadCount(int convId) async {
    final count = messages.id.count();
    final query = selectOnly(messages)
      ..addColumns([count])
      ..where(messages.conversationId.equals(convId) & messages.isRead.equals(false));
    final result = await query.getSingle();
    return result.read(count) ?? 0;
  }

  // 标记消息已读
  Future<void> markAsRead(int convId, int userId) async {
    await (update(messages)
      ..where((t) => t.conversationId.equals(convId) & t.senderId.equals(userId) & t.isRead.equals(false)))
      .write(const MessagesCompanion(isRead: Value(true)));
  }
}

八、常见问题

8.1 编译报错 "Cannot find name 'Value'"

导入 drift:

import 'package:drift/drift.dart';

8.2 代码生成失败

# 清理并重新生成
dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs

8.3 Web 端不支持

// 使用 drift_flutter 包支持 Web
import 'package:drift_flutter/drift_flutter.dart';

LazyDatabase _openConnection() {
  return driftDatabase(name: 'my_database');
}

8.4 迁移失败

onUpgrade: (Migrator m, int from, int to) async {
  await customStatement('PRAGMA foreign_keys = OFF');
  // 执行迁移
  await customStatement('PRAGMA foreign_keys = ON');
}

九、总结

drift 是 Flutter 最推荐的 SQLite ORM:

  • 全平台支持(包括 Web)
  • 类型安全(编译时检查)
  • 响应式查询(Stream 实时更新)
  • 完整迁移支持
  • Flutter 官方推荐

适合微信级别的大型应用,是企业级项目的首选方案。