Flutter嵌入式数据库插件couchbase_lite_dart的使用
引言
Couchbase Lite 是一个功能强大的嵌入式 NoSQL 数据库,可在移动设备上本地运行。这是一个基于 Couchbase Lite C 库(CBL_C)的 Dart 端口,使用了 dart:ffi。
警告: 该项目仍处于早期开发阶段,API 尚未稳定,可能会有破坏性更改!
欢迎参与测试、文档编写和开发工作。您可以查看如何贡献。
功能清单
以下是该插件的主要功能列表:
数据库
- 基本操作:
- 打开、关闭、复制、压缩、删除数据库。
- 批量操作(类似于事务)。
- 变更通知和文档变更通知。
- 缓冲通知。
文档
- CRUD 操作:
- 创建、读取、更新、删除文档。
- 保存冲突处理程序。
- 文档过期并自动清理。
- 使用 Fleece API 直接访问二进制数据。
查询
- 基于 Couchbase Server 的 N1QL 语言构建的查询语言。
- 支持查询参数、解释查询结果、监听查询变化。
- 支持值索引或全文搜索(FTS)。
复制
- 后台任务同步本地数据库与远程服务器上的另一个数据库。
- 支持基本认证和会话认证。
- 支持拉取/推送过滤器。
- 状态监听器。
- 冲突解决回调。
Blob
- 基于内容的 API 创建和读取。
- 基于流的 API。
平台支持
- Windows: 包含在包中,Beta 状态。
- Android: 需要手动配置。
- 可以从仓库构建共享库:
https://github.com/Rudiksz/couchbase-lite-C.git
,使用feature/dart
分支或匹配版本的标签。 - 或者从以下链接下载预构建库:
https://drive.google.com/drive/folders/1qiLdB64kq-IEsp6hFgvSqG80bzaI33Jf
。 将共享库放置在项目的android/app/src/main/jniLibs/
文件夹中。
- 可以从仓库构建共享库:
- iOS、macOS: 不支持。
示例与用法
在 main.dart
中初始化 Dart 和 C 之间的回调。
Cbl.init();
然后执行以下代码:
/// 创建或打开数据库
var db = Database('name', directory: 'path/to/directory');
// 创建文档
var doc = Document("docid", data: {'name': 'John Doe'});
db.saveDocument(doc);
// 读取不可变文档
var doc1 = db.getDocument('docid');
doc1.properties = {'foo': 'bar'}; // -> 抛出 DatabaseException
// 获取可变副本
var mutDoc = doc1.mutableCopy;
mutDoc.jsonProperties = {'foo': 'bar'}; // -> OK
db.saveDocument(mutDoc);
// 或者直接获取可变文档
var doc2 = db.getMutableDocument('testdoc3');
doc2.jsonProperties = {'foo': 'bar8'};
db.saveDocument(doc2);
// 查询
// 编译查询
final q = Query(db, 'SELECT * WHERE foo=$VALUE');
q.setParameters = {'VALUE': 'bar'};
// 可选地将其变为“实时查询”
q.addChangeListener((ResultSet results) {
print('New query results: ');
while(results.next()){
final row = results.rowDict;
print(row.json);
}
});
// 执行查询
var results = q.execute();
// 复制器
// 创建复制器
var replicator = Replicator(
db,
endpointUrl: 'ws://localhost:4984/remoteDB/',
username: 'testuser',
password: 'password', // 或
// 'sessionId': 'dfhfsdyf8dfenfajfoadnf83c4dfhdfad3228yrsefd',
);
// 设置状态监听器
replicator.addChangeListener((status) {
print('Replicator status: ' + status.activityLevel.toString());
});
// 启动复制器
replicator.start();
完整的示例请参考 example 文件夹,包括 Fleece API 的使用。
完整示例代码
以下是一个完整的示例代码,展示如何使用 couchbase_lite_dart
插件。
// Copyright (c) 2020, Rudolf Martincsek. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math';
import 'package:cblc_flutter/fleece.dart';
import 'package:couchbase_lite_dart/couchbase_lite_dart.dart';
import 'package:flutter/material.dart';
void main() async {
Cbl.init();
var db = Database("testdb");
print(db.isOpen);
await Future.delayed(Duration(seconds: 1));
var doc = Document("testdoc");
doc.jsonProperties = testjson;
db.saveDocument(doc);
runApp(MaterialApp(
title: 'Couchbase Lite Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DatabaseView(db),
));
}
class DatabaseView extends StatefulWidget {
final Database db;
const DatabaseView(this.db, {Key key}) : super(key: key);
[@override](/user/override)
_DatabaseViewState createState() => _DatabaseViewState();
}
class _DatabaseViewState extends State<DatabaseView> {
ValueNotifier<String> _explain = ValueNotifier('');
ValueNotifier<bool> _queryChanged = ValueNotifier(false);
ValueNotifier<bool> _resultsChanged = ValueNotifier(false);
var _results = [];
var _selectedDoc = '';
static const _select = 'SELECT meta.id, * ';
var queryText = TextEditingController(text: "");
String prevQueryText;
Query query;
List documents = [1];
[@override](/user/override)
void initState() {
super.initState();
explain();
queryText.addListener(() => _queryChanged.value = queryText.text != query?.queryString);
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CBL - c'),
),
body: Row(
children: [
sideBar(),
Expanded(
child: ValueListenableBuilder(
builder: (_, __, ___) => FleeceView(
docId: _selectedDoc,
db: widget.db,
),
valueListenable: _resultsChanged,
),
),
],
),
);
}
sideBar() {
return SizedBox(
width: 300,
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Text("Query: SELECT meta.id, *",
style: Theme.of(context).textTheme.headline5)),
queryBox(),
SliverToBoxAdapter(
child: Card(
child: ValueListenableBuilder(
valueListenable: _explain,
builder: (_, value, __) => Text(value),
),
),
),
SliverToBoxAdapter(
child: Text("Results",
style: Theme.of(context).textTheme.headline5)),
buildDocumentList(),
],
),
);
}
queryBox() {
return SliverToBoxAdapter(
child: Card(
elevation: 8,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded(
child: TextField(
controller: queryText,
minLines: 4,
maxLines: 4,
decoration: InputDecoration(
labelText: "WHERE, ORDER, LIMIT, etc",
alignLabelWithHint: true,
),
),
),
],
),
Row(
children: [
Spacer(),
OutlineButton(child: Text("Explain"), onPressed: explain),
Spacer(flex: 4),
ValueListenableBuilder(
builder: (_, value, __) => OutlineButton(
child: Text("Execute"),
onPressed: value ? execute : null,
),
valueListenable: _queryChanged,
),
Spacer(),
],
),
],
),
),
);
}
buildDocumentList() {
return ValueListenableBuilder(
valueListenable: _resultsChanged,
builder: (_, __, ___) =>
(_results?.isNotEmpty ?? false)
? SliverList(
delegate: SliverChildBuilderDelegate(
(_, i) => Card(
child: ListTile(
selected: _results[i]['id'] == _selectedDoc,
title: Text(_results[i].toString()),
dense: true,
onTap: () {
_selectedDoc = _results[i]['id'];
_resultsChanged.value = !_resultsChanged.value;
},
),
),
childCount: _results.length,
),
)
: SliverToBoxAdapter(child: Icon(Icons.dashboard)),
);
}
updateQuery() {
if (prevQueryText != queryText.text) {
prevQueryText = queryText.text;
try {
query = Query(widget.db, _select + queryText.text);
query.addChangeListener(updateItems);
} on DatabaseException catch (e) {
_explain.value = e.message;
query = null;
}
}
}
explain() {
updateQuery();
_explain.value = query?.explain() ?? _explain.value;
}
execute() {
updateQuery();
if (query != null) {
query.execute();
_queryChanged.value = false;
}
}
updateItems(List change) {
print("************* CHANGES RECEIVED*************");
print(change);
_results = change ?? [];
_resultsChanged.value = !_resultsChanged.value;
}
}
class SliverListHeaderDelegate extends SliverPersistentHeaderDelegate {
SliverListHeaderDelegate({
@required this.minHeight,
@required this.maxHeight,
@required this.child,
this.shrunkChild,
});
final double minHeight;
final double maxHeight;
final Widget child;
final Widget shrunkChild;
[@override](/user/override)
double get minExtent => minHeight;
[@override](/user/override)
double get maxExtent => max(maxHeight, minHeight);
[@override](/user/override)
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) =>
shrinkOffset < 100
? SizedBox.expand(child: child)
: (shrunkChild ?? child);
[@override](/user/override)
bool shouldRebuild(SliverListHeaderDelegate oldDelegate) => false;
}
const testjson = '''
{
"TESTDOC": "THIS DOCUMENT IS RECREATED EVERY TIME THE APP RUNS",
"int": 10,
"double": 2.2,
"bool": true,
"string": "hello world!",
"list": [
1,
"2",
3.3
],
"map": {
"first": [1, 2,3],
"second": "hello again",
"third": false,
"dart": {"is": "cool"}
},
"map1": {
"list": [
{"first": [1, 2, 3, 4]},
{"second": [6, 7, 8]},
true,
10,
2.5
]
}
}
''';
更多关于Flutter嵌入式数据库插件couchbase_lite_dart的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter嵌入式数据库插件couchbase_lite_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
couchbase_lite_dart
是一个用于在 Flutter 应用程序中使用 Couchbase Lite 的插件。Couchbase Lite 是一个轻量级的嵌入式 NoSQL 数据库,适用于移动和桌面应用程序。它支持本地数据存储和同步,非常适合需要在离线状态下工作的应用程序。
以下是如何在 Flutter 项目中使用 couchbase_lite_dart
插件的步骤:
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 couchbase_lite_dart
插件的依赖:
dependencies:
flutter:
sdk: flutter
couchbase_lite_dart: ^1.0.0 # 检查最新版本并替换
然后运行 flutter pub get
来安装依赖。
2. 初始化数据库
在使用 Couchbase Lite 之前,你需要初始化数据库。通常,你可以在 main.dart
文件中的 main
函数中进行初始化。
import 'package:couchbase_lite_dart/couchbase_lite_dart.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化 Couchbase Lite
await CouchbaseLiteDart.init();
runApp(MyApp());
}
3. 打开或创建数据库
你可以打开现有的数据库或创建一个新的数据库。以下是一个示例:
import 'package:couchbase_lite_dart/couchbase_lite_dart.dart';
Future<Database> openDatabase() async {
final database = await Database.openOrCreate('my_database');
return database;
}
4. 插入文档
你可以使用 Document
类来插入文档到数据库中:
Future<void> insertDocument(Database database) async {
final document = MutableDocument()
..setString('type', 'user')
..setString('name', 'John Doe')
..setInt('age', 30);
await database.saveDocument(document);
}
5. 查询文档
你可以使用 Query
类来查询数据库中的文档:
Future<void> queryDocuments(Database database) async {
final query = QueryBuilder.select([SelectResult.all()])
.from(DataSource.database(database))
.where(Expression.property('type').equalTo(Expression.string('user')));
final resultSet = await query.execute();
for (final result in resultSet) {
final document = result.toMap();
print(document);
}
}
6. 更新文档
你可以通过获取现有的文档并更新其内容来更新文档:
Future<void> updateDocument(Database database) async {
final document = await database.getDocument('document_id');
if (document != null) {
final mutableDocument = document.toMutable();
mutableDocument.setInt('age', 31);
await database.saveDocument(mutableDocument);
}
}
7. 删除文档
你可以通过文档 ID 来删除文档:
Future<void> deleteDocument(Database database) async {
await database.deleteDocument('document_id');
}
8. 同步数据
Couchbase Lite 支持与远程数据库的同步。你可以使用 Replicator
类来设置同步:
Future<void> startReplication(Database database) async {
final url = Uri.parse('ws://your_couchbase_server:4984/your_database');
final target = URLEndpoint(url);
final config = ReplicatorConfiguration(database, target)
..replicatorType = ReplicatorType.pushAndPull
..continuous = true;
final replicator = Replicator(config);
replicator.start();
}
9. 关闭数据库
在应用程序退出或不再需要数据库时,关闭数据库是一个好习惯:
Future<void> closeDatabase(Database database) async {
await database.close();
}
10. 处理错误
在使用 Couchbase Lite 时,可能会遇到各种错误。确保在代码中处理这些错误:
try {
await database.saveDocument(document);
} catch (e) {
print('Error saving document: $e');
}