目录
[📂 1. 简介](#📂 1. 简介)
[1.1 Drift vs sqflite](#1.1 Drift vs sqflite)
[1.2 Drift vs Hive / Sembast vs SharedPreferences](#1.2 Drift vs Hive / Sembast vs SharedPreferences)
[2. 🔱 核心架构设计](#2. 🔱 核心架构设计)
[3. 💠 使用步骤](#3. 💠 使用步骤)
[3.1 添加依赖](#3.1 添加依赖)
[3.2 插入数据](#3.2 插入数据)
[3.3 查询数据](#3.3 查询数据)
[3.4 Drift 核心操作配套表](#3.4 Drift 核心操作配套表)
[1. 为什么有"终结方法"?](#1. 为什么有“终结方法”?)
[2. 响应式编程](#2. 响应式编程)
[3.5 运行代码生成](#3.5 运行代码生成)
[4. ⚛️ 工程化封装](#4. ⚛️ 工程化封装)
[4.1 Bean 层:定义表结构](#4.1 Bean 层:定义表结构)
[4.2 DAO 层:封装业务逻辑](#4.2 DAO 层:封装业务逻辑)
[4.3 DB 层:数据库引擎与迁移](#4.3 DB 层:数据库引擎与迁移)
[4.4 Manager 层:单例管理](#4.4 Manager 层:单例管理)
[5. ✅ 小结](#5. ✅ 小结)
📂 1. 简介
在 Flutter 开发中,选择一个合适的本地数据库方案至关重要。从早期的 sqflite 到 NoSQL 的 Hive,再到如今功能强大的 Drift,开发者们一直在追求更高效、更安全的持久化方案。
本文将带大家深度实战 Drift (原 Moor),通过 Bean -> DB -> DAO -> Manager 的分层设计,构建一套可直接用于生产环境的数据库模块。
在开始代码之前,我们先通过两张对比表看看 Drift 的江湖地位。
1.1 Drift vs sqflite
|----------|-----------------------------|----------------------|
| 特性 | Drift | sqflite |
| 类型安全 | 编译时检查,代码自动生成实体类,减少运行时错误 | 手动解析 Map,字符串硬编码,易出错 |
| 查询构建 | 强大的 Fluent API,无需手写 SQL | 必须手写 SQL 字符串,复杂查询维护难 |
| 迁移管理 | 内置自动迁移与版本策略,简单清晰 | 手动编写升级脚本,维护成本极高 |
| 开发效率 | 自动生成 CRUD,效率翻倍 | 繁琐的样板代码,开发慢 |
-
Drift 适合复杂项目,提供了 类型安全、高效查询 和 自动迁移 支持,能显著提高开发效率;
-
sqflite更适合简单的数据库需求,但对于复杂数据库操作和迁移,维护成本较高。
1.2 Drift vs Hive / Sembast vs SharedPreferences
|----------|------------------------|------------------------------------|-----------------------|
| 特性 | Drift | Hive / Sembast | SharedPreferences |
| 数据模型 | 关系型 (RDBMS),支持表、外键 | NoSQL 键值对 Hive或文档存储 Sembast,适合轻量存储 | 键值对存储,适合配置和简单数据 |
| 响应式 | 原生支持Stream 监听 | 支持监听,但复杂联动较弱 | 不支持查询与监听 |
| 适用场景 | 中大型、复杂业务逻辑项目 | 轻量缓存、小型项目 | 配置项、用户偏好设置 |
- Drift:Drift 提供了类型安全、自动迁移和响应式流支持,是中大型 Flutter 应用的首选。
2. 🔱 核心架构设计
为了保证模块的可维护性,我们采用分层架构:
-
Bean (Table):定义表结构;
-
DB (AppDatabase):数据库配置、连接与迁移逻辑;
-
DAO (Data Access Object):隔离业务 SQL 逻辑;
-
Manager (DBManager):全局单例,屏蔽初始化细节。

3. 💠 使用步骤
3.1 添加依赖
# pubspec.yaml
dependencies:
# 提供了在设备上查找常用文件目录的方法,如获取应用的文档目录、临时目录等。
path_provider: ^2.1.3
# # SQLite 数据库插件,提供了对 SQLite 数据库的访问和操作功能。
# sqflite: ^2.4.2
# Drift 是一个强大的 Flutter 和 Dart 的数据库库,简化了与 SQLite 数据库的交互。
drift: ^2.16.0
# SQLite 的 Flutter 插件,提供了 SQLite 数据库的本地支持。
sqlite3_flutter_libs: ^0.5.0
# 提供了对文件和目录路径进行操作的功能,如拼接路径、获取文件名、获取父目录等。
path: ^1.9.1
# ⚠️ 必须添加以下开发依赖,否则无法生成 .g.dart 代码
dev_dependencies:
# Drift 的代码生成器,负责解析你的 Table 定义
drift_dev: ^2.16.0
# 运行代码生成的通用工具
build_runner: ^2.4.9
3.2 插入数据
void addNote() async {
await DBManager.noteDao.insertNote(
NotesCompanion.insert(
title: '会议记录',
content: '讨论 Flutter 数据库方案',
time: Value(DateTime.now().millisecondsSinceEpoch),
),
);
}
3.3 查询数据
DBManager.noteDao.getAllNotes().then((notes) {
for (var note in notes) {
logD(tag: "DB Note:", note.toString());
}
});
3.4 Drift 核心操作配套表
|----------|-----------------|-----------------------------------|----------------------|---------------------|
| 操作类型 | 启动方法(开始) | 常见中间方法(加工) | 终结方法(发令枪) | 返回值类型 |
| 查询 | select(table) | where(), orderBy(), limit() | .get() | Future<List<T>> |
| 查询 | select(table) | where(), orderBy() | .watch() | Stream<List<T>> |
| 查询 | select(table) | where() | .getSingle() | Future<T> |
| 插入 | into(table) | 无 | .insert(companion) | Future<int> (新ID) |
| 删除 | delete(table) | where() | .go() | Future<int> (行数) |
| 更新 | update(table) | where() | .write(companion) | Future<int> (行数) |
| 更新 | update(table) | 无 | .replace(entity) | Future<bool> |
1. 为什么有"终结方法"?
Drift 的查询逻辑遵循:启动 -> 加工 -> 发令枪。
-
启动:select() / into() / delete()。
-
加工:where() / orderBy()。
-
发令枪:.get() (一次性)、.watch() (流)、.go() (执行)。 只有扣动"发令枪",SQL 才会真正执行。
2. 响应式编程
通过 watchAllNotes() 配合 Flutter 的 StreamBuilder,你可以实现数据变动时 UI 的无感刷新。这在开发笔记应用、聊天列表等场景下非常高效。
3.5 运行代码生成
在终端执行以下命令,让 build_runner 为你生成那几千行枯燥的样板代码:
flutter pub run build_runner build --delete-conflicting-outputs
Tips: 建议将生成的 .g.dart 文件提交到 Git,这样同事拉下代码后无需运行 build 命令即可编译。
4. ⚛️ 工程化封装
4.1 Bean 层:定义表结构
Drift 通过 Dart 类来定义 SQL 表。注意 () 语法,它实际上是 Drift 的构建器模式。
///
/// Description: 记录表
/// CreateDate: 2026/1/4 17:33
/// Author: agg
///
class Notes extends Table {
IntColumn get id => integer().autoIncrement()(); // 主键,自增
IntColumn get time => integer().withDefault(Constant(-1))(); // 记录生成时间
TextColumn get title => text().withLength(min: 1, max: 255)(); // 标题
TextColumn get content => text()(); // 内容
}
4.2 DAO 层:封装业务逻辑
DAO 负责具体的增删改查,利用 Drift 的流式 API,代码极度丝滑。
part 'note_dao.g.dart';
///
/// Description: 记录表 数据访问对象
/// CreateDate: 2026/1/4 17:36
/// Author: agg
///
@DriftAccessor(tables: [Notes])
class NoteDao extends DatabaseAccessor<AppDatabase> with _$NoteDaoMixin {
NoteDao(super.db);
// 插入
Future<int> insertNote(NotesCompanion note) => into(notes).insert(note);
// 查询全部
Future<List<Note>> getAllNotes() => select(notes).get();
// 监听数据流 (用于 UI 实时刷新)
Stream<List<Note>> watchAllNotes() => select(notes).watch();
// 更新
Future<bool> updateNote(Note note) => update(notes).replace(note);
// 删除
Future<int> deleteNote(int id) =>
(delete(notes)..where((t) => t.id.equals(id))).go();
}
4.3 DB 层:数据库引擎与迁移
这里包含了底层的连接配置。我们特别针对 Android 的目录规范做了优化,将 .sqlite 文件放在标准的 databases 目录下。
part 'app_database.g.dart'; // Drift 自动生成部分
///
/// Description: 应用数据库
/// CreateDate: 2026/1/4 17:39
/// Author: agg
///
@DriftDatabase(tables: [Notes], daos: [NoteDao])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1; // 生产环境升级表结构时需自增
// 这里的迁移策略可根据需求扩展,如 onCreate, onUpgrade
@override
MigrationStrategy get migration => MigrationStrategy(
// 当数据库文件【第一次】在手机上创建时执行
onCreate: (m) async {
// m 是间接操作数据库的工具
// createAll() 会扫描 @DriftDatabase 里的 tables 列表,自动执行所有的 CREATE TABLE 语句
await m.createAll();
},
// 当 schemaVersion 增加时(比如从 1 变 2),会触发这个方法
onUpgrade: (m, from, to) async {
if (from < 2) {
// 执行升级逻辑,比如给 Notes 表加个字段
// await m.addColumn(notes, notes.type);
}
},
);
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
String path = "";
if (Platform.isAndroid) {
// 获取 /data/data/包名/ 根目录
final dbFolder = await getApplicationSupportDirectory();
// 向上级找并进入 databases 文件夹
path = p.join(dbFolder.parent.path, 'databases', 'app_database.sqlite');
} else {
// iOS 等其他平台保持原样
final dbFolder = await getApplicationDocumentsDirectory();
path = p.join(dbFolder.path, 'app_database.sqlite');
}
return NativeDatabase(
File(path),
// 将 logStatements 设为 true:可以看到生成的 SQL 语句,生产环境建议关闭 SQL 日志打印
logStatements: true,
// 默认情况下,SQLite 在写入时会锁定数据库。开启 WAL (Write-Ahead Logging) 模式可以实现"读写并发",显著提升性能
setup: (rawDb) {
// 开启预写日志模式,提升并发性能
rawDb.execute('PRAGMA journal_mode = WAL;');
// 开启外键约束
rawDb.execute('PRAGMA foreign_keys = ON;');
},
);
});
}
4.4 Manager 层:单例管理
对外提供统一的静态入口。
///
/// Description: 数据库管理类,运行前可选执行Runner初始化:flutter pub run build_runner build --delete-conflicting-outputs
/// CreateDate: 2026/1/4 17:55
/// Author: agg
///
class DBManager {
// 1. 私有静态实例
static final DBManager _instance = DBManager._internal();
// 2. 数据库实例
late final AppDatabase _db;
// 3. 工厂构造函数返回单例
factory DBManager() => _instance;
// 4. 私有构造函数完成初始化
DBManager._internal() {
_db = AppDatabase();
}
// 5. 静态快捷访问方式
// 使用 instance 获取能确保单例已创建,逻辑更清晰
static NoteDao get noteDao => _instance._db.noteDao;
}
5. ✅ 小结
Drift 的设计哲学是将 SQL 的强大与 Dart 的安全完美结合。通过本篇的模块化封装,我们不仅隔离了底层复杂度,还为后续的业务扩展打下了坚实基础。
另外,由于本人能力有限,如有错误,敬请批评指正,谢谢。