Flutter Couchbase集成插件couchify的使用

Flutter Couchbase集成插件couchify的使用

Couchify 是一个为 Flutter 开发者提供的 Couchbase Lite API。其目标是让 Flutter 的 API 尽可能接近 Couchbase Lite 提供的官方 Android (Java) API,以便熟悉该 API 的用户可以轻松使用 Flutter API。该插件使用了 Couchbase Lite Android EE 2.8.5。目前,该插件仅支持 Android 平台,不支持 iOS。

开始使用

1. 添加依赖

pubspec.yaml 文件中添加 couchify 插件依赖:

dependencies:
  couchify: ^0.1.0

2. 设置最小 SDK 版本

在应用级别的 build.gradle 文件(通常是 android/app/build.gradle)中将 minSdkVersion 设置为 19:

android {
    ...
    
    defaultConfig {
        ...
        minSdkVersion 19
        ...
    }
    
    ...
}

3. 修改 AndroidManifest.xml

AndroidManifest.xml 文件中(通常位于 android/src/main/AndroidManifest.xml),添加 xmlns:tools 命名空间,并在 <application> 元素中添加 tools:replace 属性:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"      <!-- 添加 xmlns:tools -->
    package=...>
    <application
        android:label=...
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher"
        tools:replace="android:label">                  <!-- 添加 tools:replace -->

4. 导入模块

在需要使用的 Dart 文件中导入 couchify 模块:

import 'package:couchify/couchify.dart';

现在你已经准备好开始使用 Couchify 了!

示例用法

以下是一个完整的示例代码,展示了如何初始化 Couchbase Lite、创建数据库、保存文档、查询文档等操作:

// 初始化 Couchbase Lite
await CouchbaseLite.init();

// 创建数据库配置,默认目录为应用程序文件目录
// 如果需要自定义目录,可以使用 setDirectory 方法
DatabaseConfiguration configuration = DatabaseConfiguration();

// 打开或创建名为 "testDb" 的数据库
Database db = await Database.open("testDb", configuration);

// 打印数据库在设备上的路径
// 输出示例:/data/data/<package-name>/files/testDb.cblite2/
print(await db.getPath());

// 创建一个带有指定 ID 的可变文档,并设置键值对
MutableDocument doc =
    MutableDocument.id("doc1").setString("key1", "value1");

// 创建一个可变数组并添加两个值,然后将其添加到文档中
MutableArray array = MutableArray().addString("value1").addInt(1234);
doc.setArray("an array", array);

// 创建一个可变字典并添加两个键值对,然后将其添加到文档中
MutableDictionary dictionary = MutableDictionary()
    .setString("key1", "value1")
    .setBoolean("key2", false);
doc.setDictionary("a dictionary", dictionary);

// 将文档保存到数据库中
await db.save(doc);

// 使用文档 ID 获取文档
Document? fetchedDoc = await db.getDocument("doc1");

if (fetchedDoc != null) {
  // 将文档转换为可变文档并修改它
  MutableDocument updatedDoc = fetchedDoc.toMutable();
  updatedDoc.setString("key1", "new-value");
  updatedDoc.setDate("current_date", DateTime.now());
  updatedDoc.setValue("a null value", null);

  // 将更新后的文档保存回数据库以更新现有文档
  await db.save(updatedDoc);

  // 使用 QueryBuilder API 构建查询(类似于官方 Couchbase Lite Android API)
  Query query = QueryBuilder.select(
          [SelectResult.expression(Meta.id), SelectResult.all()])
      .from(DataSource.database(db));

  // 执行查询以获取结果集
  ResultSet rs = await query.execute();

  // 遍历结果集并打印每个结果的内容作为 Map
  await for (Result result in rs.getStream()) {
    print(result.toMap());
  }
}

支持的功能

该插件当前支持以下功能:

  1. 打开或创建本地数据库

    • 可以指定默认目录或自定义目录。
  2. CRUD 操作

    • 创建、更新和保存可变文档。
    • 根据 ID 获取文档。
    • 删除文档。
  3. 删除数据库

  4. 同时打开多个不同的数据库

    • 通过创建多个 Database 对象实现。
  5. 支持数组和字典类型

    • 包括它们的可变版本。
  6. 文档支持的数据类型

    • num, bool, String, null, DateTime, Array, Dictionary, List, Map<String, dynamic>
    • 最后两种类型必须只包含支持的类型。Blob 当前不支持。
  7. 部分支持查询

    • 支持基本的 select, from, wherejoin 子句。
    • 支持数组函数、变量表达式和数组表达式。
    • 不支持 group by, order by, limit 子句和全文搜索。

以下是一个示例查询:

QueryBuilder.select([
    SelectResult.expression(Expression.property("name").from("airline")),
    SelectResult.expression(Expression.property("callsign").from("airline")),
    SelectResult.expression(
        Expression.property("destinationairport").from("route")),
    SelectResult.expression(Expression.property("stops").from("route"))
  ]).from(DataSource.database(db).as("airline")).join([
    Join.join(DataSource.database(db).as("route"))
  ]).where(Expression.property("type")
      .from("route")
      .equalTo(Expression.string("route"))
      .and(Expression.property("type")
          .from("airline")
          .equalTo(Expression.string("airline")))
      .and(Expression.property("sourceairport")
          .from("route")
          .equalTo(Expression.string("RIX"))));

路线图

以下功能目前尚未支持,但属于开发计划:

  • 批量事务
  • 数据库和文档更改监听器
  • 同步网关和点对点同步支持
  • 查询中的 order by, group by, limit 子句
  • 全文搜索
  • 索引

完整示例 Demo

以下是一个完整的示例代码,展示了一个简单的待办事项列表应用,使用 Couchify 插件与 Couchbase Lite 进行交互:

import 'package:couchify_example/database_manager.dart';
import 'package:flutter/material.dart';
import 'dart:async';

import 'package:couchify/couchify.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  static const String dbName = "todoListDb";
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: TodoListViewer(dbName: dbName),
    );
  }
}

class TodoListViewer extends StatefulWidget {
  final String dbName;

  const TodoListViewer({Key? key, required this.dbName}) : super(key: key);

  [@override](/user/override)
  State<TodoListViewer> createState() => _TodoListViewerState();
}

class _TodoListViewerState extends State<TodoListViewer> {
  bool _initialized = false;
  DatabaseManager? _dbManager;
  List<TodoListItem>? _todoList;
  int _selectedItemsCount = 0;
  bool _searchMode = false;
  final _queryController = TextEditingController();
  final _dateFormat = DateFormat(DateFormat.ABBR_MONTH_DAY);

  [@override](/user/override)
  void initState() {
    super.initState();
    CouchbaseLite.init().then((value) {
      var configuration = DatabaseConfiguration();
      return Database.open(widget.dbName, configuration);
    }).then((database) {
      _dbManager = DatabaseManager(database);
      return _fetchTodoList();
    }).then((value) {
      setState(() {
        _initialized = true;
      });
    });
  }

  Future _fetchTodoList() async {
    var items = await _dbManager!.getItems();
    setState(() {
      _todoList = items;
      _selectedItemsCount = 0;
    });
  }

  Future _addItem(String title, String tags) async {
    var item = TodoListItem(
        title: title, tags: tags.split(","), created: DateTime.now());
    await _dbManager?.addItem(item);
    await _fetchTodoList();
    print(_todoList);
  }

  Future _deleteItems() async {
    await Future.forEach(_todoList!.where((item) => item.isSelected),
        (TodoListItem item) async {
      return _dbManager?.deleteItem(item);
    });
    if (_searchMode) {
      await _queryItems(_queryController.text);
    } else {
      await _fetchTodoList();
    }
  }

  Future _updateItem(TodoListItem item, String title, String tags) async {
    item.title = title;
    item.tags = tags.split(",");
    await _dbManager?.updateItem(item);
    await _fetchTodoList();
  }

  Future _queryItems(String query) async {
    var items = await _dbManager!.queryByTitleOrTag(query);
    setState(() {
      _todoList = items;
      _selectedItemsCount = 0;
    });
  }

  void _showCreateTodoListItemModal() {
    var titleController = TextEditingController();
    var tagsController = TextEditingController();

    showModalBottomSheet<void>(
      context: context,
      isScrollControlled: true,
      builder: (BuildContext context) {
        return Padding(
          padding: MediaQuery.of(context).viewInsets,
          child: Container(
            height: 250,
            color: Colors.amber,
            child: Center(
              child: Padding(
                padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    const Text(
                      'Create New Item',
                      style: TextStyle(
                        fontSize: 20.0,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(
                      height: 12.0,
                    ),
                    TextField(
                      controller: titleController,
                      decoration: const InputDecoration(
                        hintText: "Title",
                      ),
                    ),
                    TextField(
                      controller: tagsController,
                      decoration: const InputDecoration(
                        hintText: "Tags (comma-separated)",
                      ),
                    ),
                    const SizedBox(
                      height: 12.0,
                    ),
                    ElevatedButton(
                      child: const Text('Create'),
                      onPressed: () {
                        _addItem(titleController.text, tagsController.text);
                        Navigator.pop(context);
                      },
                    )
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );
  }

  void _showEditTodoListItemModal(TodoListItem item) {
    var titleController = TextEditingController(text: item.title!);
    var tagsController = TextEditingController(text: item.tags!.join(','));

    showModalBottomSheet<void>(
      context: context,
      isScrollControlled: true,
      builder: (BuildContext context) {
        return Padding(
          padding: MediaQuery.of(context).viewInsets,
          child: Container(
            height: 250,
            color: Colors.amber,
            child: Center(
              child: Padding(
                padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    const Text(
                      'Edit Item',
                      style: TextStyle(
                        fontSize: 20.0,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(
                      height: 12.0,
                    ),
                    TextField(
                      controller: titleController,
                      decoration: const InputDecoration(
                        hintText: "Title",
                      ),
                    ),
                    TextField(
                      controller: tagsController,
                      decoration: const InputDecoration(
                        hintText: "Tags (comma-separated)",
                      ),
                    ),
                    const SizedBox(
                      height: 12.0,
                    ),
                    ElevatedButton(
                      child: const Text('Update'),
                      onPressed: () {
                        _updateItem(
                            item, titleController.text, tagsController.text);
                        Navigator.pop(context);
                      },
                    )
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _searchMode
          ? AppBar(
              title: TextField(
                style: const TextStyle(color: Colors.white),
                controller: _queryController,
                cursorColor: Colors.white,
                autofocus: true,
                onChanged: (queryString) {
                  _queryItems(queryString);
                },
                decoration: const InputDecoration(
                  hintText: "Search by title or tag...",
                  hintStyle: TextStyle(color: Colors.white),
                  border: InputBorder.none,
                ),
              ),
              leading: IconButton(
                onPressed: () {
                  setState(() {
                    _searchMode = false;
                    _fetchTodoList();
                  });
                },
                icon: const Icon(Icons.arrow_back),
              ),
              actions: _selectedItemsCount > 0
                  ? <Widget>[
                      IconButton(
                        icon: const Icon(Icons.delete),
                        onPressed: () {
                          _deleteItems();
                        },
                      )
                    ]
                  : null,
            )
          : AppBar(
              title: const Text("To-do List App Example"),
              actions: <Widget>[
                _selectedItemsCount > 0
                    ? IconButton(
                        icon: const Icon(Icons.delete),
                        onPressed: () {
                          _deleteItems();
                        },
                      )
                    : IconButton(
                        onPressed: () {
                          _queryController.clear();
                          setState(() {
                            _searchMode = true;
                          });
                        },
                        icon: const Icon(Icons.search),
                      ),
              ],
            ),
      body: !_initialized
          ? const Center(child: CircularProgressIndicator())
          : RefreshIndicator(
              onRefresh: () async {
                return;
              },
              child: ListView.builder(
                itemBuilder: (context, index) {
                  return CheckboxListTile(
                    onChanged: (checked) {
                      setState(() {
                        _todoList![index].isSelected = checked ?? false;
                        _selectedItemsCount +=
                            _todoList![index].isSelected ? 1 : -1;
                      });
                    },
                    value: _todoList![index].isSelected,
                    controlAffinity: ListTileControlAffinity.leading,
                    secondary:
                        Text(_dateFormat.format(_todoList![index].created!)),
                    title: Text(_todoList![index].title!),
                    subtitle:
                        Text('Tags: ' + _todoList![index].tags!.join(', ')),
                  );
                },
                itemCount: _todoList!.length,
              ),
            ),
      floatingActionButton: _initialized
          ? (_selectedItemsCount <= 1
              ? (_selectedItemsCount == 0
                  ? FloatingActionButton(
                      onPressed: _showCreateTodoListItemModal,
                      child: const Icon(Icons.add))
                  : FloatingActionButton(
                      onPressed: () {
                        var item = _todoList!
                            .where((element) => element.isSelected)
                            .single;
                        _showEditTodoListItemModal(item);
                      },
                      child: const Icon(Icons.edit)))
              : null)
          : null,
    );
  }
}

class TodoListItem {
  final String title;
  final List<String> tags;
  final DateTime created;
  bool isSelected;

  TodoListItem({
    required this.title,
    required this.tags,
    required this.created,
    this.isSelected = false,
  });
}

更多关于Flutter Couchbase集成插件couchify的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter Couchbase集成插件couchify的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


Couchbase Lite 是一个轻量级的、嵌入式的 NoSQL 数据库,适用于移动和桌面应用程序。Flutter 是一个流行的跨平台移动应用开发框架,而 couchify 是一个用于在 Flutter 应用中集成 Couchbase Lite 的插件。

以下是如何在 Flutter 项目中使用 couchify 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 couchify 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  couchify: ^latest_version  # 请替换为最新版本

然后运行 flutter pub get 来获取依赖。

2. 初始化 Couchbase Lite

在你的 Flutter 应用中,你需要初始化 Couchbase Lite 数据库。

import 'package:couchify/couchify.dart';

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

  // 初始化 Couchbase Lite
  await Couchify.init();

  runApp(MyApp());
}

3. 创建和打开数据库

你可以创建一个新的数据库或者打开一个已经存在的数据库。

Future<void> openDatabase() async {
  final database = await Couchify.database("my_database");
  await database.open();
}

4. 插入文档

你可以向数据库中插入文档。

Future<void> insertDocument(Database database) async {
  final document = MutableDocument()
    ..setString("type", "user")
    ..setString("name", "John Doe")
    ..setInt("age", 30);

  await database.saveDocument(document);
}

5. 查询文档

你可以使用查询来检索文档。

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.allResults()) {
    print(result.toMap());
  }
}

6. 更新文档

你可以更新现有的文档。

Future<void> updateDocument(Database database) async {
  final document = await database.document("document_id");
  if (document != null) {
    final mutableDocument = document.toMutable()
      ..setString("name", "Jane Doe");

    await database.saveDocument(mutableDocument);
  }
}

7. 删除文档

你可以从数据库中删除文档。

Future<void> deleteDocument(Database database) async {
  final document = await database.document("document_id");
  if (document != null) {
    await database.deleteDocument(document);
  }
}

8. 关闭数据库

当你不再需要数据库时,可以关闭它。

Future<void> closeDatabase(Database database) async {
  await database.close();
}

9. 处理同步(可选)

Couchbase Lite 支持与远程 Couchbase Server 进行数据同步。你可以配置同步器来实现数据的双向同步。

Future<void> startReplication(Database database) async {
  final endpoint = URLEndpoint(Uri.parse("ws://localhost:4984/my_database"));
  final replicatorConfig = ReplicatorConfiguration(
    database: database,
    target: endpoint,
    replicatorType: ReplicatorType.pushAndPull,
  );

  final replicator = Replicator(replicatorConfig);
  replicator.start();
}

10. 处理错误

在使用 Couchbase Lite 时,建议处理可能出现的错误。

try {
  await database.saveDocument(document);
} catch (e) {
  print("Error saving document: $e");
}

11. 清理资源

在应用退出时,确保清理 Couchbase Lite 资源。

@override
void dispose() {
  database.close();
  super.dispose();
}

12. 运行应用

现在你可以运行你的 Flutter 应用,并使用 couchify 插件来管理 Couchbase Lite 数据库。

flutter run
回到顶部