Flutter for OpenHarmony 实战:Sembast — 丝滑体验的纯 Dart NoSQL 数据库
摘要: 本文介绍了在Flutter for OpenHarmony开发中使用Sembast NoSQL数据库的实战指南。Sembast作为纯Dart实现的数据库,无需原生依赖,在鸿蒙系统上表现稳定,特别适合中小型应用。文章详细讲解了环境配置方法,包括获取鸿蒙沙箱路径和初始化数据库。重点展示了三个核心功能:基于Map的灵活数据存取、响应式实时监听以及事务处理。最后通过构建离线文章收藏中心的完整示例,
Flutter for OpenHarmony 实战:Sembast — 丝滑体验的纯 Dart NoSQL 数据库

前言
在 Flutter for OpenHarmony 开发中,我们经常需要处理结构化但又具有高度灵活性的数据。虽然 SQLite 是老牌选择,但其繁琐的 SQL 语句和固定的表结构对于快速迭代的互联网项目来说显得有些笨重。
Sembast 是目前跨平台生态中最受欢迎的纯 Dart 实现的 NoSQL 数据库。它不需要任何复杂的 C 语言级二进制依赖(FFI),直接基于 JSON 格式进行存储。这意味着它在鸿蒙全版本系统上都有着极其稳定的表现。本文将带你探索这一“随处可用”的数据存储利器。
二、为什么 Sembast 是中小型鸿蒙应用的首选?
1.1 零原生依赖,极致稳定 🛡️
Sembast 不依赖底层系统的数据库驱动。它将数据持久化为简单的文本流。在鸿蒙设备开启了“极速模式”或系统内核大幅更新时,Sembast 不会因为 FFI 兼容问题而导致应用启动异常。
1.2 强大的反应式 (Reactive) 支持
它原生支持 Stream 监听。一旦数据库某个 Record 发生变动,UI 层的 StreamBuilder 会自动捕捉并刷新,无需手动刷新页面。
三、配置环境 📦
引入核心包及其配套的文件系统适配器:
dependencies:
sembast: ^3.7.4
path_provider: ^2.1.2
path: ^1.9.0
# ⚠️ 关键:必须覆盖原有的 path_provider 以支持鸿蒙沙箱路径获取
dependency_overrides:
path_provider_ohos:
git:
url: https://gitee.com/openharmony-sig/flutter_packages.git
path: packages/path_provider/path_provider_ohos
3.1 初始化鸿蒙侧数据库工厂
由于 Sembast 是纯 Dart 实现,它不需要在鸿蒙 Native 层配置任何 .so(不像 ObjectBox)。你只需要获取到一个合法的鸿蒙沙箱路径即可:
import 'package:sembast/sembast_io.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
Future<Database> openOhosDb() async {
// 1. 获取鸿蒙应用的私有文档目录
// 💡 技巧:path_provider_ohos 会自动映射到鸿蒙 el2 级加密存储
final dir = await getApplicationDocumentsDirectory();
// 2. 确保目录存在
await dir.create(recursive: true);
// 3. 拼接数据库物理路径
final dbPath = join(dir.path, 'harmony_vault.db');
// 4. 打开并返回数据库实例
return await databaseFactoryIo.openDatabase(dbPath);
}

四、核心功能:3 个高阶数据操作场景
3.1 基于 Map 的灵活存取 (Dynamic Schema)
无需建表,直接存储任何结构的 JSON 数据。
void saveConfig(Database db) async {
final store = intMapStoreFactory.store('ohos_settings');
// 💡 技巧:直接插入 Map,系统会自动生成自增 ID
await store.add(db, {
'theme': 'deep_blue',
'notifications_enabled': true,
'last_sync': DateTime.now().toIso8601String(),
});
}

3.2 响应式实时监听 (Stream Query)
让选中的记录集始终与 UI 保持同步。
Stream<List<RecordSnapshot<int, Map<String, dynamic>>>> watchTasks(Database db) {
final store = intMapStoreFactory.store('tasks');
final finder = Finder(sortOrders: [SortOrder('priority', false)]);
// 💡 技巧:这是构建即时聊天或动态列表的利器
return store.query(finder: finder).onSnapshots(db);
}

3.3 事务处理与并发安全 (Transactions)
确保鸿蒙端在多任务同时操作数据库时的原子性。
await db.transaction((txn) async {
await store.record(1).put(txn, {'status': 'processed'});
await auditStore.add(txn, {'log': 'ID 1 updated'});
});

5.1 极致的跨平台兼容性 🚀
Sembast 是目前在鸿蒙开发中最推荐的复杂数据库方案。
- 原因:它没有任何 Native 绑定代码。即便鸿蒙系统从 4.0 升级到 5.0,或者从 ARM 架构切换到其他架构,Sembast 都能因为“纯 Dart”的特性实现无缝运行,完全避开了 ObjectBox 等库面临的
.so库丢失或二进制冲突问题。
5.2 处理鸿蒙后台挂起时的断电风险
- 💡 技巧:Sembast 的
sembast_io实现使用了“顺序追加”日志模式。即使在写入中途发生断电或应用强杀,它也能在下次启动时自动修复损坏的末端,极大地保护了鸿蒙用户的数据完整性。建议对关键操作显式使用await db.flush()以确保数据落地。
六、完整实战示例:构建鸿蒙应用“全平台”离线文章收藏中心
我们将构建一个具备高性能的收藏管理模块:它能够离线缓存文章详情,并提供秒级的多条件模糊查询能力。
import 'package:flutter/material.dart';
import 'package:sembast/sembast.dart';
import 'sembast_helper.dart';
class OhosContentVaultPage extends StatefulWidget {
const OhosContentVaultPage({super.key});
State<OhosContentVaultPage> createState() => _OhosContentVaultPageState();
}
class _OhosContentVaultPageState extends State<OhosContentVaultPage> {
final _store = intMapStoreFactory.store('collected_articles');
String _searchKeyword = '';
// 💡 实战:构建动态过滤的 Stream
Stream<List<RecordSnapshot<int, Map<String, dynamic>>>> _getFilteredStream(
Database db) {
Filter? filter;
if (_searchKeyword.isNotEmpty) {
// 同时匹配标题或标签
filter = Filter.or([
Filter.matches('title', _searchKeyword),
Filter.matches('tags', _searchKeyword),
]);
}
final finder = Finder(
filter: filter,
sortOrders: [SortOrder('createdAt', false)],
);
return _store.query(finder: finder).onSnapshots(db);
}
Future<void> _addSampleArticle() async {
final db = await SembastHelper.database;
final samples = [
{'title': '鸿蒙内核深度解析', 'url': 'ohos.dev/kernel', 'tags': '底层'},
{'title': 'Dart 3.0 新特性', 'url': 'dart.dev/v3', 'tags': '语法'},
{
'title': 'Flutter 鸿蒙适配指南',
'url': 'flutter_ohos.org/guide',
'tags': '工程'
},
];
// 随机选一个存入
final article = samples[DateTime.now().second % samples.length];
await _store.add(db, {
...article,
'createdAt': DateTime.now().millisecondsSinceEpoch,
});
}
void _onSearchSubmit(String kw) {
setState(() {
_searchKeyword = kw;
});
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('内容同步中心 (真实 DB)'),
elevation: 0,
backgroundColor: Colors.white,
foregroundColor: Colors.black,
),
body: FutureBuilder<Database>(
future: SembastHelper.database,
builder: (context, dbSnapshot) {
if (!dbSnapshot.hasData)
return const Center(child: CircularProgressIndicator());
return Column(
children: [
_buildSearchBar(),
Expanded(
child: StreamBuilder<
List<RecordSnapshot<int, Map<String, dynamic>>>>(
stream: _getFilteredStream(dbSnapshot.data!),
builder: (context, streamSnapshot) {
final articles = streamSnapshot.data ?? [];
if (articles.isEmpty) {
return Center(
child: Text(
_searchKeyword.isEmpty ? '你的收藏夹空空如也' : '未找到相关文章'),
);
}
return ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: articles.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, index) {
final data = articles[index].value;
return ListTile(
contentPadding:
const EdgeInsets.symmetric(vertical: 8),
title: Text(data['title'] as String,
style:
const TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(data['url'] as String,
style: TextStyle(color: Colors.grey[600])),
trailing: Chip(
label: Text(data['tags'] as String,
style: const TextStyle(fontSize: 10)),
backgroundColor: Colors.blue[50],
),
onLongPress: () async {
await _store
.record(articles[index].key)
.delete(dbSnapshot.data!);
},
);
},
);
},
),
),
_buildNote(),
],
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _addSampleArticle,
child: const Icon(Icons.bookmark_add_outlined),
),
);
}
Widget _buildSearchBar() {
return Container(
padding: const EdgeInsets.all(16),
child: TextField(
onSubmitted: _onSearchSubmit,
onChanged: (v) {
if (v.isEmpty) _onSearchSubmit('');
},
decoration: InputDecoration(
hintText: '搜索本地离线文章 (按回车提交)...',
prefixIcon: const Icon(Icons.search),
filled: true,
fillColor: Colors.grey[100],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide.none,
),
),
),
);
}
Widget _buildNote() {
return Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.blue[50],
border: Border(top: BorderSide(color: Colors.blue[100]!)),
),
child: const Row(
children: [
Icon(Icons.info_outline, color: Colors.blue, size: 20),
SizedBox(width: 12),
Expanded(
child: Text(
'已开启鸿蒙级 FFI 零依赖持久化:搜索是在真实的本地 .db 文件中利用 Filter.matches 完成的。',
style: TextStyle(color: Colors.blue, fontSize: 12),
),
),
],
),
);
}
}

七、总结
Sembast 为 Flutter for OpenHarmony 开发者提供了一种极其优雅的数据交互方式。它不需要你成为 SQL 专家,只需关注你的 Dart 模型。在鸿蒙系统这个快速进化的舞台上,Sembast 正是那个能让你“轻装上阵”的数据库伙伴。
如果你追求开发速度与运行稳定性的平衡,请务必给 Sembast 一个机会。
🌐 欢迎加入开源鸿蒙��平台社区:开源鸿蒙跨平台开发者社区
更多推荐



所有评论(0)