琼海市网站建设_网站建设公司_加载速度优化_seo优化
2026/1/5 21:55:57 网站建设 项目流程

目录

📂 1. 简介

1.1 Drift vs sqflite

1.2 Drift vs Hive / Sembast vs SharedPreferences

2. 🔱 核心架构设计

3. 💠 使用步骤

3.1 添加依赖

3.2 插入数据

3.3 查询数据

3.4 Drift 核心操作配套表

1. 为什么有“终结方法”?

2. 响应式编程

3.5 运行代码生成

4. ⚛️ 工程化封装

4.1 Bean 层:定义表结构

4.2 DAO 层:封装业务逻辑

4.3 DB 层:数据库引擎与迁移

4.4 Manager 层:单例管理

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 的安全完美结合。通过本篇的模块化封装,我们不仅隔离了底层复杂度,还为后续的业务扩展打下了坚实基础。

另外,由于本人能力有限,如有错误,敬请批评指正,谢谢。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询