Flutter应用中使用Couchbase Lite的插件 cbl_flutter的使用

发布于 1周前 作者 yibo5220 最后一次编辑是 5天前 来自 Flutter

Flutter应用中使用Couchbase Lite的插件 cbl_flutter的使用

插件简介

cbl_flutter 是一个允许你在Flutter应用中使用Couchbase Lite的插件。Couchbase Lite是一个嵌入式的NoSQL数据库,支持多平台(Android, iOS, macOS, Windows, Linux),并且具有快速紧凑、JSON风格文档、表达式查询、可观察性以及数据同步等功能。

特点

  • 多平台:支持多个操作系统。
  • 独立Dart和Flutter:无需手动设置,只需添加包即可。
  • 快速且紧凑:使用高效的持久化数据结构。
  • JSON风格文档:无显式模式,支持深度嵌套。
  • 表达式查询:支持SQL++ (SQL for JSON),QueryBuilder,全文搜索等。
  • 可观察性:能够监听数据库、查询和数据同步的变化。
  • 数据同步:可以从/向服务器拉取和推送数据,并对同步的数据有完全控制权。

使用方法

初始化项目

pubspec.yaml文件中添加依赖:

dependencies:
  cbl_flutter: ^0.6.0 # 请根据实际情况选择版本

然后运行命令以获取并安装所有依赖项:

flutter pub get

示例代码

以下是一个完整的示例程序,展示了如何初始化Couchbase Lite,创建数据库,保存新文档,构建查询以及监控查询结果。

main.dart

// 忽略某些lint规则
// ignore_for_file: avoid_print, lines_longer_than_80_chars, diagnostic_describe_all_properties

/// 这个简单的示例应用程序允许用户查看和添加日志消息列表。
///
/// 它演示了如何:
///
/// - 初始化Couchbase Lite,
/// - 打开数据库,
/// - 创建索引,
/// - 保存新文档,
/// - 通过查询生成器构建查询,
/// - 解释查询执行方式,
/// - 监控查询变化。
library main;

import 'dart:async';

import 'package:cbl/cbl.dart';
import 'package:cbl_flutter/cbl_flutter.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

Future<void> main() async {
  await initApp();
  runApp(const MyApp());
}

// === UI ======================================================================

const spacing = 16.0;

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) => const MaterialApp(
        debugShowCheckedModeBanner: false,
        home: LogMessagesPage(),
      );
}

class LogMessagesPage extends StatefulWidget {
  const LogMessagesPage({super.key});

  @override
  State<LogMessagesPage> createState() => _LogMessagesPageState();
}

class _LogMessagesPageState extends State<LogMessagesPage> {
  List<LogMessage> _logMessages = [];
  late StreamSubscription _logMessagesSub;

  @override
  void initState() {
    super.initState();
    _logMessagesSub =
        logMessageRepository.allLogMessagesStream().listen((logMessages) {
      setState(() => _logMessages = logMessages);
    });
  }

  @override
  void dispose() {
    _logMessagesSub.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        body: SafeArea(
          child: Column(children: [
            Expanded(
              child: ListView.builder(
                // 反转ListView以便新消息立即可见,并确保页面打开时滚动到底部。
                reverse: true,
                itemCount: _logMessages.length,
                itemBuilder: (context, index) {
                  final logMessage =
                      _logMessages[_logMessages.length - 1 - index];

                  return LogMessageTile(logMessage: logMessage);
                },
              ),
            ),
            const Divider(height: 0),
            _LogMessageForm(onSubmit: logMessageRepository.createLogMessage)
          ]),
        ),
      );
}

class LogMessageTile extends StatelessWidget {
  const LogMessageTile({super.key, required this.logMessage});

  final LogMessage logMessage;

  @override
  Widget build(BuildContext context) => Padding(
        padding: const EdgeInsets.all(spacing),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              DateFormat.yMd().add_jm().format(logMessage.createdAt),
              style: Theme.of(context).textTheme.bodySmall,
            ),
            const SizedBox(height: spacing / 4),
            Text(logMessage.message)
          ],
        ),
      );
}

class _LogMessageForm extends StatefulWidget {
  const _LogMessageForm({required this.onSubmit});

  final ValueChanged<String> onSubmit;

  @override
  _LogMessageFormState createState() => _LogMessageFormState();
}

class _LogMessageFormState extends State<_LogMessageForm> {
  late final TextEditingController _messageController;
  late final FocusNode _messageFocusNode;

  @override
  void initState() {
    super.initState();
    _messageController = TextEditingController();
    _messageFocusNode = FocusNode();
  }

  @override
  void dispose() {
    _messageController.dispose();
    _messageFocusNode.dispose();
    super.dispose();
  }

  void _onSubmit() {
    final message = _messageController.text.trim();
    if (message.isEmpty) {
      return;
    }

    widget.onSubmit(message);
    _messageController.clear();
    _messageFocusNode.requestFocus();
  }

  @override
  Widget build(BuildContext context) => Padding(
        padding: const EdgeInsets.all(spacing),
        child: Row(
          children: [
            Expanded(
              child: TextField(
                decoration:
                    const InputDecoration.collapsed(hintText: 'Message'),
                autofocus: true,
                focusNode: _messageFocusNode,
                controller: _messageController,
                minLines: 1,
                maxLines: 10,
                style: Theme.of(context).textTheme.bodyMedium,
                textCapitalization: TextCapitalization.sentences,
              ),
            ),
            const SizedBox(width: spacing / 2),
            TextButton(
              onPressed: _onSubmit,
              child: const Text('Write to log'),
            )
          ],
        ),
      );
}

// === Log Message Storage =====================================================

/// 日志消息模型
abstract class LogMessage {
  String get id;
  DateTime get createdAt;
  String get message;
}

/// 包含日志消息的 [DictionaryInterface] 的实现。
///
/// [DictionaryInterface] 在 `cbl` 中由几种类型实现:
///
/// - [Document],
/// - [Dictionary],
/// - [Result].
///
/// 通过从 `cbl` API 返回的对象访问数据比将其转换为纯 Dart 对象更有效率。
/// 这些对象仅提取实际访问的数据。
class CblLogMessage extends LogMessage {
  CblLogMessage(this.dict);

  final DictionaryInterface dict;

  @override
  String get id => dict.documentId;

  @override
  DateTime get createdAt => dict.value('createdAt')!;

  @override
  String get message => dict.value('message')!;
}

extension DictionaryDocumentIdExt on DictionaryInterface {
  /// 如果是 [Document],则返回其 `id`,否则返回字段 `id` 的 [String]。
  String get documentId {
    final self = this;
    return self is Document ? self.id : self.value('id')!;
  }
}

/// [LogMessage]s 的存储库,抽象了数据存储访问。
class LogMessageRepository {
  LogMessageRepository(this.database, this.collection);

  final Database database;
  final Collection collection;

  Future<LogMessage> createLogMessage(String message) async {
    final doc = MutableDocument({
      'type': 'logMessage',
      'createdAt': DateTime.now(),
      'message': message,
    });
    await collection.saveDocument(doc);
    return CblLogMessage(doc);
  }

  Stream<List<LogMessage>> allLogMessagesStream() {
    final query = const QueryBuilder()
        .select(
          SelectResult.expression(Meta.id),
          SelectResult.property('createdAt'),
          SelectResult.property('message'),
        )
        .from(DataSource.collection(collection))
        .where(
          Expression.property('type').equalTo(Expression.value('logMessage')),
        )
        .orderBy(Ordering.property('createdAt'));

    // 打印查询执行解释信息
    Future(query.explain).then(print);

    return query.changes().asyncMap(
          (change) => change.results.asStream().map(CblLogMessage.new).toList(),
        );
  }
}

// === App Setup ===============================================================

late Database database;
late Collection logMessages;
late LogMessageRepository logMessageRepository;

/// 初始化全局应用程序状态。
Future<void> initApp() async {
  WidgetsFlutterBinding.ensureInitialized();

  await TracingDelegate.install(DevToolsTracing());

  await CouchbaseLiteFlutter.init();

  // 取消注释下面一行可以在每次启动应用时重置数据库。
  // await Database.remove('example');

  database = await Database.openAsync('example');
  logMessages = await database.createCollection('logMessages');

  // 创建索引以加速查询,特别是那些按确切 `type` 过滤并按 `createdAt` 排序的查询。
  await logMessages.createIndex(
    'type+createdAt',
    ValueIndex([
      ValueIndexItem.property('type'),
      ValueIndexItem.property('createdAt'),
    ]),
  );

  logMessageRepository = LogMessageRepository(database, logMessages);
}

此示例展示了如何在Flutter应用中使用cbl_flutter插件进行基本的CRUD操作。你可以根据自己的需求扩展这个基础框架,比如增加更多的业务逻辑或优化UI界面。希望这对你的开发有所帮助!如果你有任何问题或需要进一步的帮助,请随时提问。


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

1 回复

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


针对您提到的Flutter未知功能插件cbl_flutter的探索使用,由于cbl_flutter并非一个广为人知的插件(至少在公开发布的信息中未找到具体细节),我无法提供确切的官方文档或详细功能描述。不过,我可以基于一般Flutter插件的使用方式,给出一个探索和使用未知Flutter插件的基本框架代码示例。

请注意,以下代码是基于假设cbl_flutter插件存在并遵循Flutter插件的基本使用规则编写的。如果cbl_flutter有特定的配置要求或API,您需要根据其官方文档进行调整。

1. 添加依赖

首先,在您的pubspec.yaml文件中添加cbl_flutter作为依赖:

dependencies:
  flutter:
    sdk: flutter
  cbl_flutter: ^x.y.z  # 替换为实际版本号

然后运行flutter pub get来安装依赖。

2. 导入插件

在您需要使用该插件的Dart文件中导入它:

import 'package:cbl_flutter/cbl_flutter.dart';

3. 初始化与使用

假设cbl_flutter插件有一个初始化方法initialize和一个主要功能方法performAction,您可以这样使用它:

import 'package:flutter/material.dart';
import 'package:cbl_flutter/cbl_flutter.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('cbl_flutter 插件探索'),
        ),
        body: Center(
          child: MyHomePage(),
        ),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String result = '';

  @override
  void initState() {
    super.initState();
    // 初始化插件
    _initializePlugin();
  }

  Future<void> _initializePlugin() async {
    try {
      // 假设插件有一个初始化方法
      await CblFlutter.initialize();
      setState(() {
        result = '插件初始化成功';
      });
    } catch (e) {
      setState(() {
        result = '插件初始化失败: ${e.message}';
      });
    }
  }

  Future<void> _performAction() async {
    try {
      // 假设插件有一个主要功能方法
      var response = await CblFlutter.performAction();
      setState(() {
        result = '操作成功: $response';
      });
    } catch (e) {
      setState(() {
        result = '操作失败: ${e.message}';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text(result),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _performAction,
          child: Text('执行插件功能'),
        ),
      ],
    );
  }
}

注意事项

  1. 文档与API:务必查看cbl_flutter插件的官方文档,了解其具体功能和API。
  2. 错误处理:在实际使用中,应添加更完善的错误处理逻辑。
  3. 权限与配置:如果插件需要特定的权限或配置,请确保在AndroidManifest.xmlInfo.plist中正确配置。

由于cbl_flutter的具体细节未知,上述代码仅为一个探索性框架。如果您有cbl_flutter的具体文档或API参考,请根据实际情况进行调整。

回到顶部