【maaath】开源鸿蒙Flutter持久化实战:留住“状态”
在 Flutter for OpenHarmony 项目里,网络数据、页面切换和组件复用往往不是最难的部分,真正影响体验的,常常是“状态是否能留住”。本文围绕Flutter for OpenHarmony 跨平台技术为跨平台应用接入本地持久化能力;我在实践时最关注的并不是“能不能写进去”,而是“写进去后是否真的能在 OpenHarmony 设备侧稳定恢复”。因此,本文不仅讲 Flutter 层的封
开源鸿蒙Flutter持久化实战
标签:开源鸿蒙、Flutter for OpenHarmony、跨平台框架、本地持久化、数据缓存
作者:maaath
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
摘要
在 Flutter for OpenHarmony 项目里,网络数据、页面切换和组件复用往往不是最难的部分,真正影响体验的,常常是“状态是否能留住”。用户刚收藏完内容、刚切换完配置,如果应用重启后全部丢失,这种割裂感会非常明显。本文围绕 Flutter for OpenHarmony 跨平台技术 展开,结合一个实际运行的 OpenHarmony 示例工程,完成两件事:
- 为跨平台应用接入本地持久化能力;
- 实现用户配置与收藏数据缓存,并在开源鸿蒙模拟器上完成运行验证。
我在实践时最关注的并不是“能不能写进去”,而是“写进去后是否真的能在 OpenHarmony 设备侧稳定恢复”。因此,本文不仅讲 Flutter 层的封装,也会说明它在鸿蒙侧最终依赖的是 @ohos.data.preferences 能力,这一点很关键。
为什么在 Flutter for OpenHarmony 中优先做持久化
Flutter for OpenHarmony 的价值,在于它让一套 Dart UI 与业务逻辑可以在 OpenHarmony 上快速落地,减少重复开发成本。对于资讯、服务、内容分发和用户中心类应用来说,本地缓存至少承担三类职责:
- 保存用户偏好:例如主题、语言、通知开关;
- 保存轻量业务状态:例如最近选择项、筛选条件;
- 保存收藏/书签数据:让用户跨会话继续使用。
如果这些数据全部依赖远端接口恢复,不仅首屏慢,而且离线体验差。基于这一点,本文选择 shared_preferences 作为 Flutter 层入口,再通过 OpenHarmony 侧插件把数据落到系统 Preferences 中。这种方案的优点是清晰、成熟,而且适合“配置类”和“轻量结构化数据”场景。
从平台能力看,OpenHarmony 官方文档明确提供了 Preferences 数据持久化接口,适合存储键值型数据;而 Flutter 官方生态中,shared_preferences 的使用方式也非常稳定。这两者组合起来,几乎就是跨平台工程里最自然的一条路径。参考资料可见:
- OpenHarmony 开发文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-preferences-V5
- Flutter 官方文档:https://docs.flutter.dev/
- shared_preferences 插件说明:https://pub.dev/packages/shared_preferences
- OpenAtom OpenHarmony 社区:https://www.openharmony.cn/
本文示例工程的思路
本文使用的是一个 Flutter for OpenHarmony 示例工程,页面结构包含 Home、Discover、Favorites、Profile 四个标签页。为了避免文章空泛,我把目标控制得很具体:
- 收藏页中的内容需要被持久化;
- 个人页中的用户状态需要具备扩展空间;
- OpenHarmony 端必须能真正运行,而不是只停留在 Dart 层演示。
从结果看,这种拆分是合理的。收藏数据属于典型的“轻量对象列表”,可以序列化成 JSON 存入本地;用户配置则适合继续复用同一套存储入口。换句话说,先把“收藏缓存”打通,后续扩展“用户设置缓存”时成本会非常低。
Flutter 层:先把收藏服务做成可复用单例
在 Flutter for OpenHarmony 工程里,我建议把本地持久化能力封装成服务类,而不是散落在页面中。下面这段代码是本文示例的核心:一个单例 FavoritesService,负责完成初始化、添加收藏、取消收藏和本地恢复。
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/data_item.dart';
class FavoritesService extends ChangeNotifier {
static final FavoritesService _instance = FavoritesService._internal();
factory FavoritesService() => _instance;
FavoritesService._internal();
static const String _storageKey = 'favorites_data';
final List<DataItem> _favorites = [];
bool _initialized = false;
List<DataItem> get favorites => List.unmodifiable(_favorites);
bool get isInitialized => _initialized;
bool isFavorite(int id) {
return _favorites.any((item) => item.id == id);
}
Future<void> init() async {
if (_initialized) return;
await _loadFromPrefs();
_initialized = true;
}
Future<void> toggleFavorite(DataItem item) async {
if (isFavorite(item.id)) {
_favorites.removeWhere((e) => e.id == item.id);
} else {
_favorites.add(item);
}
await _saveToPrefs();
notifyListeners();
}
Future<void> _loadFromPrefs() async {
final prefs = await SharedPreferences.getInstance();
final jsonStr = prefs.getString(_storageKey);
if (jsonStr == null || jsonStr.isEmpty) return;
_favorites.clear();
final List<dynamic> data = jsonDecode(jsonStr);
for (final item in data) {
_favorites.add(DataItem.fromJson(item as Map<String, dynamic>));
}
}
Future<void> _saveToPrefs() async {
final prefs = await SharedPreferences.getInstance();
final jsonStr = jsonEncode(_favorites.map((e) => e.toJson()).toList());
await prefs.setString(_storageKey, jsonStr);
}
}
这段代码的设计重点有三个:
1. 用单例保证状态一致
收藏本质上是全局状态。如果每个页面都 new 一个服务实例,状态会被切碎。单例可以保证收藏列表在标签页切换时始终一致。
2. 用 JSON 保存对象列表
shared_preferences 更适合存基础类型,因此我把 List<DataItem> 转成 JSON 字符串保存。这是 Flutter 跨平台项目里非常常见、也足够稳妥的处理方式。
3. 修改状态后立即落盘
我个人比较倾向“操作后立刻持久化”,虽然这会多几次 I/O,但对于收藏这类轻量数据来说收益更大:即使用户突然退出应用,也不容易丢状态。
数据模型:尽量保持序列化友好
本地缓存如果想稳定,模型必须简单、可逆。下面这个 DataItem 模型就是一个适合本地 JSON 序列化的结构:
class DataItem {
final int id;
final String title;
final String description;
final String status;
final String createdAt;
DataItem({
required this.id,
required this.title,
required this.description,
required this.status,
required this.createdAt,
});
factory DataItem.fromJson(Map<String, dynamic> json) {
return DataItem(
id: json['id'] as int? ?? 0,
title: json['title'] as String? ?? '',
description: json['description'] as String? ?? '',
status: json['status'] as String? ?? 'pending',
createdAt: json['created_at'] as String? ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'description': description,
'status': status,
'created_at': createdAt,
};
}
}
这里没有引入复杂反射,也没有额外的序列化生成工具,目的就是让 Flutter for OpenHarmony 工程在迁移和调试时足够直观。对于文章读者来说,这种写法也更容易上手。
页面接入:让收藏数据“看得见、删得掉、重启能恢复”
在 FavoritesPage 中,页面通过 ListenableBuilder 监听 FavoritesService,这样本地状态一旦变化,界面会立即刷新。这个思路非常适合 Flutter for OpenHarmony,因为 UI 更新逻辑仍然保持 Flutter 原生开发习惯,不需要为了平台适配改写页面结构。
class FavoritesPage extends StatefulWidget {
const FavoritesPage({super.key});
State<FavoritesPage> createState() => _FavoritesPageState();
}
class _FavoritesPageState extends State<FavoritesPage> {
final FavoritesService _favoritesService = FavoritesService();
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: _favoritesService,
builder: (context, child) {
final favorites = _favoritesService.favorites;
return Scaffold(
appBar: AppBar(title: const Text('Favorites')),
body: favorites.isEmpty
? const Center(child: Text('No favorites yet'))
: ListView.builder(
itemCount: favorites.length,
itemBuilder: (context, index) {
final item = favorites[index];
return ListTile(
title: Text(item.title),
subtitle: Text(item.description),
trailing: IconButton(
icon: const Icon(Icons.favorite, color: Colors.red),
onPressed: () => _favoritesService.toggleFavorite(item),
),
);
},
),
);
},
);
}
}
这一层实现完成后,Flutter 侧就已经具备完整收藏缓存能力了。用户收藏的对象会以 JSON 形式存储,本次会话和下次会话都能恢复。这也是我认为最值得优先落地的一步:它足够小,但对用户体验的提升很直接。
OpenHarmony 侧:shared_preferences 最终落到 Preferences
如果只讲 Flutter 代码,文章会显得“跨平台味道不够”。为了把链路讲完整,有必要说明一下 OpenHarmony 侧插件是如何落地的。
在本文工程中,shared_preferences_ohos 插件最终通过 @ohos.data.preferences 完成真实持久化。也就是说,Flutter 调用 SharedPreferences.getInstance() 后,并不是凭空生效,而是由 OpenHarmony 插件桥接到系统 Preferences:
import data_preferences from '@ohos.data.preferences'
const PREFERENCES_NAME = 'FlutterSharedPreferences';
onAttachedToEngine(binding: FlutterPluginBinding): void {
let promise = data_preferences.getPreferences(
binding.getApplicationContext(),
PREFERENCES_NAME
);
promise.then((object) => {
this.preferences = object;
})
}
put(key: string, value: ESObject): Promise<void> {
this.preferences?.put(key, value);
return this.preferences!.flush();
}
这部分让我更愿意把 Flutter for OpenHarmony 视为“真正可落地的跨平台方案”,而不是简单的界面移植。因为它既保留了 Flutter 的编程体验,又能在 OpenHarmony 设备上调用原生数据能力,形成一条完整的存储链路。
模拟器运行验证:这一步不能省
本文没有停留在代码展示层面,而是完成了 OpenHarmony 模拟器侧的安装与启动验证。验证链路包括:
- 使用 hvigor 构建 HAP;
- 通过
hdc安装到模拟器; - 成功启动应用主 Ability;
- 在界面中看到服务台页面与缓存相关内容。
从工程实践角度看,我认为“能编过”与“能跑起来”是两个概念。前者说明语法没问题,后者才说明平台能力、签名、打包与运行路径全部打通。本文示例已经完成了这一层验证,因此更适合作为读者上手 Flutter for OpenHarmony 持久化改造的起点。
运行截图(OpenHarmony 模拟器)
下图为代码在 OpenHarmony 模拟器上的运行截图,可作为本文方案成功落地的验证证据:

读者实践建议
如果你也要在 Flutter for OpenHarmony 项目中加入本地持久化,我建议按下面顺序推进:
- 先定数据边界:优先缓存轻量数据,不要一开始就把复杂数据库场景塞进来;
- 先封装服务层:把收藏、配置等逻辑放进 Service,而不是散在页面里;
- 先验证恢复链路:测试“收藏后退出应用,再进入是否恢复”;
- 再扩展配置项:例如主题、语言、通知、最近浏览记录;
- 最后再考虑同步策略:本地缓存与远端账户同步是下一阶段问题,不必一步到位。
如果需要托管自己的跨平台代码仓库,建议统一使用 AtomGit:https://atomgit.com 。这样在团队协作、版本管理和成果展示时会更规范,也符合当前国产开源协作生态。
结语
回到题目本身:为开源鸿蒙跨平台工程集成本地持久化能力,实现用户配置与收藏数据缓存,并完成开源鸿蒙模拟器运行验证,这件事在 Flutter for OpenHarmony 中完全可行,而且实现路径并不复杂。
在我看来,这类改造最有价值的地方,不是多写了几百行代码,而是把“跨平台应用在 OpenHarmony 上是否具备真实可用性”往前推进了一步。Flutter 层负责组织业务状态,OpenHarmony 层负责承接系统存储能力,两者结合后,用户才能真正感知到“这个应用是连续的、可信赖的”。
如果你正在做内容类、服务类或者用户中心较重的 Flutter for OpenHarmony 项目,我会很建议先把本地持久化打牢。因为一旦这层能力稳定下来,后续你做离线优化、弱网恢复、个性化推荐,都会轻松很多。
感谢各位阅读!
更多推荐




所有评论(0)