前言
Flutter 目前比较好用的 sqlite 数据库 orm 框架就是drift (以前叫作moor),由于dart语言不支持反射,这个框架使用了dart代码生成器,自动生成代码。这个数据库框架的优点是支持全平台使用,此框架通过使用datr语言的 ffi 功能(相当于JAVA的jni)来调用 sqlite 动态库 实现数据库操作,
Web平台是通过 wasm(浏览器可以运行的二进制文件,可由C、C++、rust、go 等语言编译而来)来调用sqlite, Web平台的sqlite数据库文件则通过 indexed db 虚拟文件系统保存。下面来介绍一下使用方法。
官方文档
https://drift.simonbinder.eu/docs/platforms/
先导入依赖
dependencies:
drift: ^2.4.2
sqlite3_flutter_libs: ^0.5.0
#sqlcipher_flutter_libs: ^0.5.1
path_provider: ^2.0.0
path: ^1.8.3
dev_dependencies:
drift_dev: ^2.4.1
build_runner: ^2.3.3
这里是对每个包的作用的快速概述:
- drift: 这是定义大多数 api 的核心包
- sqlite3_flutter_libs: 提供 sqlite 动态库,如果要加密数据库,请使用 sqlcipher_flutter_libs
- sqlcipher_flutter_libs: 提供 sqlcipher (加密版sqlite) 动态库,如果要加密数据库请添加此依赖,并移除sqlite3_flutter_libs依赖(共存会冲突)
- path_provider 和 path:用于寻找合适的位置来存放数据库。 由 Flutter 和 Dart 团队维护
- drift_dev:drift自动生成代码工具、 不会包含在最终应用程序中。
- build_runner: 代码生成的通用工具,由 Dart 团队维护
下面仅示例使用加密版sqlite:
创建一个文件 databases.dart
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:logger/logger.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
//引入自动生成的代码,刚开始会报错,运行 flutter pub run build_runner build 即可
part 'database.g.dart';
//数据库密码
const _encryptionPassword = 'password';
//定义一个表
class Notes extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get content => text()();
}
//这个注解告诉 Drift 创建一个包含 我们上面定义的 Notes 表的数据库类(数据库操作类)
@DriftDatabase(tables: [Notes])
class MyEncryptedDatabase extends _$MyEncryptedDatabase {
MyEncryptedDatabase() : super(_openDatabase());
//设置数据库版本。
@override
int get schemaVersion => 1;
//数据库迁移方法
@override
MigrationStrategy get migration {
return MigrationStrategy(
beforeOpen: (details) async {},
);
}
}
//自定义打开数据库的方法
QueryExecutor _openDatabase() {
return LazyDatabase(() async {
//通过路径提供器获取目录以存放数据库文件
final path = (Platform.isMacOS || Platform.isIOS)
? await getApplicationDocumentsDirectory()
: await getApplicationSupportDirectory();
//这是数据库文件。
final dbFile = p.join(path.path, 'databases', 'app.db');
Logger().d(dbFile);
return NativeDatabase(
File(dbFile),
setup: (db) {
//这个语法检查数据库是否已经加密
final result = db.select('pragma cipher_version');
if (result.isEmpty) {
throw UnsupportedError(
'this database needs to run with sqlcipher, but that library is '
'not available!',
);
}else{
Logger().d("数据库已加密");
}
//数据库的密码。
final escapedKey = _encryptionPassword;
//通过密码打开数据库。
db.execute("pragma key = '$escapedKey'");
},
);
});
}
加密版本的数据库需要覆盖打开数据库的方法,创建一个 setup.dart
import 'package:sqlcipher_flutter_libs/sqlcipher_flutter_libs.dart';
import 'dart:ffi';
import 'package:sqlite3/open.dart';
setupDatabases() {
open
..overrideFor(OperatingSystem.android, openCipherOnAndroid)
..overrideFor(OperatingSystem.iOS, DynamicLibrary.process);
// 其他平台不需要覆盖
}
测试
运行生成代码命令
flutter pub run build_runner build
然后即可运行
Future<void> main() async {
//确保组件树初始化
WidgetsFlutterBinding.ensureInitialized();
//覆盖数据库打开方法
setupDatabases();
//创建数据库
final _database = MyEncryptedDatabase();
//操作数据库
_database.into(_database.notes).insert(NotesCompanion.insert(content: "test"));
//查询
var r = await _database.select(_database.notes).get();
Logger().d(r);
runApp(const MyApp());
// runApp(const AutocompleteExampleApp());
}
Web 平台
这是native平台使用方法,
建议web平台另外创建一个分支,否则依赖会报错
web平台使用方法如下:
web平台的_openDatabase方法不同
import 'package:dio/dio.dart';
import 'package:drift/drift.dart';
import 'package:drift/wasm.dart';
import 'package:sqlite3/wasm.dart';
part 'database.g.dart';
// ...more code
QueryExecutor _openDatabase() {
return LazyDatabase(() async {
// 加载 sqlite3.wasm
final response = await Dio().getUri(Uri.parse('sqlite3.wasm'),options: Options(
responseType:ResponseType.bytes
));
// 创建一个由 IndexedDb 支持的虚拟文件系统
final fs = await IndexedDbFileSystem.open(dbName: 'my_app');
final sqlite3 = await WasmSqlite3.load(
response.data,
SqliteEnvironment(fileSystem: fs),
);
// 然后,在该文件夹中打开一个数据库。
return WasmDatabase(sqlite3: sqlite3, path: 'app.db');
});
}
测试, web平台不需要调用 setupDatabases()
- 运行生成代码命令
flutter pub run build_runner build
- 把 sqlite3.wasm 放到web目录下, 必须使用定制的 sqlite3.wasm , 下载地址https://github.com/simolus3/sqlite3.dart/releases/tag/sqlite3-1.9.1
然后即可运行
Future<void> main() async {
//确保组件树初始化
WidgetsFlutterBinding.ensureInitialized();
//创建数据库
final _database = MyEncryptedDatabase();
//操作数据库
_database.into(_database.notes).insert(NotesCompanion.insert(content: "test"));
//查询
var r = await _database.select(_database.notes).get();
Logger().d(r);
runApp(const MyApp());
// runApp(const AutocompleteExampleApp());
}