Flutter 三方库 shared_preferences 的 OpenHarmony 鸿蒙化适配实践
摘要:本文介绍了Flutter三方库shared_preferences在OpenHarmony平台的适配实践。详细讲解了从环境配置、项目初始化到依赖集成的完整流程,提供了键值对存储的核心API封装示例和业务场景应用方法。文章包含StorageManager工具类实现和UserPreferences业务封装示例,帮助开发者快速实现本地数据持久化功能。通过这份实践指南,开发者可以轻松将shared_
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
Flutter 三方库 shared_preferences 的 OpenHarmony 鸿蒙化适配实践
引言
在移动应用开发中,本地数据持久化是一个基础且核心的需求。shared_preferences作为Flutter生态中最流行的键值对存储库,因其简单易用的API设计而被广泛应用于用户配置、缓存数据、状态保存等场景。随着OpenHarmony生态的快速发展,如何将shared_preferences库适配到鸿蒙平台成为了众多跨平台开发者关注的重点。本文将详细介绍在Flutter-OH项目中集成shared_preferences的完整流程,包括环境配置、依赖集成、代码实现、平台适配以及常见问题解决方案,为开发者提供一份可直接参考的实践指南。
一、环境准备与项目初始化
1.1 开发环境配置
在开始shared_preferences的鸿蒙化适配之前,需要确保开发环境满足以下要求:
- DevEco Studio 版本:4.1 及以上
- Flutter SDK 版本:3.16.0 及以上
- OpenHarmony SDK 版本:4.1.0.400 及以上
- 鸿蒙设备或模拟器:用于运行和验证代码
开发者可以通过以下命令检查Flutter和OpenHarmony的环境配置:
flutter --version
flutter doctor
1.2 创建 Flutter-OH 项目
使用Flutter命令行工具创建支持OpenHarmony的新项目:
flutter create --platforms=ohos flutter_shared_preferences_oh
cd flutter_shared_preferences_oh
项目创建完成后,目录结构将包含标准的Flutter项目文件,以及专门为OpenHarmony平台准备的ohos文件夹。
二、集成 shared_preferences 依赖
2.1 添加依赖到 pubspec.yaml
在项目的pubspec.yaml文件中添加shared_preferences依赖:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
shared_preferences: ^2.2.2
shared_preferences库是一个官方维护的Flutter插件,内部通过平台通道(Method Channel)与各平台的原生实现进行通信。对于OpenHarmony平台,该库已经提供了初步支持,但开发者仍需关注平台特定的细节问题。
2.2 获取依赖
运行以下命令下载并安装依赖包:
flutter pub get
命令执行成功后,shared_preferences及其依赖项将被添加到项目中。
三、shared_preferences 在 OpenHarmony 上的适配实践
3.1 基础使用方法
shared_preferences的核心API设计简洁明了,主要包含以下几类操作:
- 获取实例:通过
getInstance()异步方法获取SharedPreferences单例 - 存储数据:支持String、int、double、bool、List等多种数据类型
- 读取数据:通过对应的get方法读取指定key的值
- 删除数据:移除指定key的数据或清空所有数据
下面是一个完整的工具类封装示例:
import 'package:shared_preferences/shared_preferences.dart';
class StorageManager {
static final StorageManager _instance = StorageManager._internal();
factory StorageManager() => _instance;
StorageManager._internal();
Future<SharedPreferences> get _prefs async =>
await SharedPreferences.getInstance();
// 保存字符串
Future<void> setString(String key, String value) async {
final prefs = await _prefs;
await prefs.setString(key, value);
}
// 获取字符串
Future<String?> getString(String key, [String? defaultValue]) async {
final prefs = await _prefs;
return prefs.getString(key) ?? defaultValue;
}
// 保存整数
Future<void> setInt(String key, int value) async {
final prefs = await _prefs;
await prefs.setInt(key, value);
}
// 获取整数
Future<int?> getInt(String key, [int? defaultValue]) async {
final prefs = await _prefs;
return prefs.getInt(key) ?? defaultValue;
}
// 保存布尔值
Future<void> setBool(String key, bool value) async {
final prefs = await _prefs;
await prefs.setBool(key, value);
}
// 获取布尔值
Future<bool?> getBool(String key, [bool? defaultValue]) async {
final prefs = await _prefs;
return prefs.getBool(key) ?? defaultValue;
}
// 保存字符串列表
Future<void> setStringList(String key, List<String> value) async {
final prefs = await _prefs;
await prefs.setStringList(key, value);
}
// 获取字符串列表
Future<List<String>?> getStringList(String key, [List<String>? defaultValue]) async {
final prefs = await _prefs;
return prefs.getStringList(key) ?? defaultValue;
}
// 删除指定key
Future<void> remove(String key) async {
final prefs = await _prefs;
await prefs.remove(key);
}
// 清空所有数据
Future<void> clear() async {
final prefs = await _prefs;
await prefs.clear();
}
// 检查key是否存在
Future<bool> containsKey(String key) async {
final prefs = await _prefs;
return prefs.containsKey(key);
}
}
3.2 实际业务场景封装
在真实的应用开发中,通常会基于StorageManager进一步封装业务相关的存储功能。例如用户偏好设置、应用配置、缓存管理等。下面是一个用户配置管理类的示例:
import 'storage_manager.dart';
class UserPreferences {
static final UserPreferences _instance = UserPreferences._internal();
factory UserPreferences() => _instance;
UserPreferences._internal();
final StorageManager _storage = StorageManager();
static const String _keyUsername = 'username';
static const String _keyDarkMode = 'dark_mode';
static const String _keyFontSize = 'font_size';
static const String _keyTodoList = 'todo_list';
// 用户名相关操作
Future<void> setUsername(String username) async {
await _storage.setString(_keyUsername, username);
}
Future<String> getUsername() async {
return await _storage.getString(_keyUsername, '访客');
}
// 暗黑模式相关操作
Future<void> setDarkMode(bool enabled) async {
await _storage.setBool(_keyDarkMode, enabled);
}
Future<bool> getDarkMode() async {
return await _storage.getBool(_keyDarkMode, false);
}
// 字体大小相关操作
Future<void> setFontSize(double size) async {
await _storage.setDouble(_keyFontSize, size);
}
Future<double> getFontSize() async {
return await _storage.getDouble(_keyFontSize, 16.0);
}
// 待办事项列表操作
Future<void> addTodoItem(String item) async {
final currentList = await getTodoList();
currentList.add(item);
await _storage.setStringList(_keyTodoList, currentList);
}
Future<void> removeTodoItem(int index) async {
final currentList = await getTodoList();
if (index >= 0 && index < currentList.length) {
currentList.removeAt(index);
await _storage.setStringList(_keyTodoList, currentList);
}
}
Future<List<String>> getTodoList() async {
return await _storage.getStringList(_keyTodoList, []);
}
}
3.3 UI 层集成示例
下面展示如何在Flutter应用的UI层中集成shared_preferences,实现用户配置的保存与读取:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter SharedPreferences OH Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _ageController = TextEditingController();
String _savedUsername = '';
int _savedAge = 0;
bool _isDarkMode = false;
List<String> _todoList = [];
void initState() {
super.initState();
_loadPreferences();
}
Future<void> _loadPreferences() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_savedUsername = prefs.getString('username') ?? '';
_savedAge = prefs.getInt('age') ?? 0;
_isDarkMode = prefs.getBool('dark_mode') ?? false;
_todoList = prefs.getStringList('todo_list') ?? [];
});
}
Future<void> _saveUsername() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', _usernameController.text);
setState(() {
_savedUsername = _usernameController.text;
});
_usernameController.clear();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('用户名保存成功')),
);
}
}
Future<void> _saveAge() async {
final prefs = await SharedPreferences.getInstance();
final age = int.tryParse(_ageController.text) ?? 0;
await prefs.setInt('age', age);
setState(() {
_savedAge = age;
});
_ageController.clear();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('年龄保存成功')),
);
}
}
Future<void> _toggleDarkMode(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('dark_mode', value);
setState(() {
_isDarkMode = value;
});
}
Future<void> _addTodo(String todo) async {
if (todo.trim().isEmpty) return;
final prefs = await SharedPreferences.getInstance();
final newList = List<String>.from(_todoList)..add(todo);
await prefs.setStringList('todo_list', newList);
setState(() {
_todoList = newList;
});
}
Future<void> _removeTodo(int index) async {
final prefs = await SharedPreferences.getInstance();
final newList = List<String>.from(_todoList)..removeAt(index);
await prefs.setStringList('todo_list', newList);
setState(() {
_todoList = newList;
});
}
Future<void> _clearAll() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
setState(() {
_savedUsername = '';
_savedAge = 0;
_isDarkMode = false;
_todoList = [];
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('所有数据已清除')),
);
}
}
Widget build(BuildContext context) {
return Theme(
data: _isDarkMode ? ThemeData.dark() : ThemeData.light(),
child: Scaffold(
appBar: AppBar(
title: const Text('SharedPreferences OH 示例'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildCurrentData(),
const SizedBox(height: 20),
_buildUsernameInput(),
const SizedBox(height: 16),
_buildAgeInput(),
const SizedBox(height: 16),
_buildDarkModeSwitch(),
const SizedBox(height: 16),
_buildTodoList(),
const SizedBox(height: 20),
_buildClearButton(),
],
),
),
),
);
}
Widget _buildCurrentData() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'当前存储的数据',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Text('用户名: $_savedUsername'),
const SizedBox(height: 8),
Text('年龄: $_savedAge'),
const SizedBox(height: 8),
Text('暗黑模式: ${_isDarkMode ? '开启' : '关闭'}'),
const SizedBox(height: 8),
Text('待办事项数量: ${_todoList.length}'),
],
),
),
);
}
Widget _buildUsernameInput() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'保存用户名 (String)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
TextField(
controller: _usernameController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: '请输入用户名',
),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: _saveUsername,
child: const Text('保存用户名'),
),
],
),
),
);
}
Widget _buildAgeInput() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'保存年龄 (int)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
TextField(
controller: _ageController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: '请输入年龄',
keyboardType: TextInputType.number,
),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: _saveAge,
child: const Text('保存年龄'),
),
],
),
),
);
}
Widget _buildDarkModeSwitch() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'暗黑模式 (bool)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
Switch(
value: _isDarkMode,
onChanged: _toggleDarkMode,
),
],
),
),
);
}
Widget _buildTodoList() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'待办事项列表 (List<String>)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: '添加待办事项',
),
onSubmitted: (value) {
_addTodo(value);
},
),
const SizedBox(height: 12),
_todoList.isEmpty
? const Text('暂无待办事项')
: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _todoList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_todoList[index]),
trailing: IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () => _removeTodo(index),
),
);
},
),
],
),
),
);
}
Widget _buildClearButton() {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
),
onPressed: _clearAll,
child: const Text('清除所有数据'),
);
}
}
四、OpenHarmony 平台特殊适配处理
4.1 权限配置
在OpenHarmony平台上,shared_preferences不需要额外的特殊权限配置,因为它使用的是应用沙箱内的存储。但需要确保ohos/entry/src/main/module.json5文件配置正确:
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": ["phone", "tablet"],
"abilities": [
{
"name": "EntryAbility",
"srcEntrance": "./ets/entryability/EntryAbility.ets",
"description": "$string:entry_ability_desc",
"icon": "$media:app_icon",
"label": "$string:entry_ability_label",
"type": "page",
"launchType": "standard"
}
]
}
}
4.2 存储位置与数据持久化
在OpenHarmony系统中,shared_preferences的数据会被保存在应用的私有目录下,具体位置通常为:/data/app/el2/100/base/<package_name>/haps/entry/files/
数据以XML格式或类似的键值对形式存储,应用卸载时会自动清除这些数据,符合系统的安全设计原则。
4.3 平台通道实现原理
shared_preferences在OpenHarmony上的实现依赖于Flutter的平台通道机制。当Dart层调用存储方法时,数据会通过MethodChannel传递到原生侧,然后由OpenHarmony的Preferences API进行实际的读写操作。这个过程对开发者是透明的,但理解其原理有助于排查问题。
五、常见问题与解决方案
5.1 问题:数据丢失或读取失败
原因分析:
- 应用版本更新时数据迁移问题
- 设备恢复出厂设置或清除应用数据
- 异步操作顺序错误导致的竞态条件
解决方案:
- 确保在应用启动时正确初始化SharedPreferences实例
- 使用try-catch捕获可能的异常
- 对于重要数据,考虑同时使用其他持久化方案进行备份
Future<void> safeSetString(String key, String value) async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(key, value);
} catch (e) {
debugPrint('保存数据失败: $e');
// 可以在这里添加备用存储逻辑
}
}
5.2 问题:应用重启后配置不生效
原因分析:
- 没有在应用启动时正确读取已保存的配置
- 状态管理不当,UI没有正确响应数据变化
解决方案:
- 在StatefulWidget的initState方法中调用数据加载方法
- 使用setState确保UI在数据更新后重新渲染
- 考虑使用状态管理库(如Provider、Riverpod)来更好地管理配置状态
5.3 问题:大量数据存储导致性能下降
原因分析:
- shared_preferences设计用于轻量级数据存储,不适合存储大量数据
- 频繁的读写操作可能影响应用性能
解决方案:
- 对于大量结构化数据,使用SQLite或isar等数据库
- 合并多次写入操作为一次批量操作
- 对读取频繁的数据进行内存缓存
六、性能优化建议
6.1 合理使用数据类型
根据数据的实际需求选择最合适的数据类型,避免不必要的类型转换:
// 不推荐
Future<void> saveCount(int count) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('count', count.toString()); // 不必要的转换
}
// 推荐
Future<void> saveCount(int count) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('count', count); // 直接使用int类型
}
6.2 批量操作优化
当需要同时保存多个键值对时,虽然shared_preferences没有提供专门的批量API,但可以通过减少异步等待次数来优化性能:
Future<void> saveUserPreferences({
required String username,
required bool darkMode,
required double fontSize,
}) async {
final prefs = await SharedPreferences.getInstance();
// 只获取一次实例,然后进行多次操作
await prefs.setString('username', username);
await prefs.setBool('dark_mode', darkMode);
await prefs.setDouble('font_size', fontSize);
}
6.3 缓存策略
对于读取频繁且变化不频繁的数据,可以在内存中维护一份缓存,减少对磁盘的访问:
class CachedPreferences {
static final CachedPreferences _instance = CachedPreferences._internal();
factory CachedPreferences() => _instance;
CachedPreferences._internal();
SharedPreferences? _prefs;
final Map<String, dynamic> _cache = {};
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
Future<void> setString(String key, String value) async {
_cache[key] = value;
await _prefs?.setString(key, value);
}
String? getString(String key, [String? defaultValue]) {
if (_cache.containsKey(key)) {
return _cache[key] as String?;
}
final value = _prefs?.getString(key) ?? defaultValue;
_cache[key] = value;
return value;
}
}
七、运行验证
7.1 构建与运行
在OpenHarmony设备或模拟器上运行项目:
# 检查可用设备
flutter devices
# 运行应用
flutter run -d <device_id>
7.2 功能测试清单
确保测试以下功能点:
- 保存和读取字符串数据
- 保存和读取整数数据
- 保存和读取布尔值数据
- 保存和读取字符串列表
- 删除单个键值对
- 清除所有数据
- 应用重启后数据持久化验证
- 暗黑模式开关状态保持
- 待办事项列表的增删操作
八、总结与扩展
通过本文的实践,我们成功完成了shared_preferences库在Flutter-OH项目中的集成与适配。shared_preferences作为一个成熟的Flutter插件,在OpenHarmony平台上的适配相对平滑,开发者主要需要关注的是数据持久化的可靠性和性能优化。
8.1 核心要点回顾
- 环境配置:确保Flutter和OpenHarmony SDK版本兼容
- 依赖管理:在pubspec.yaml中正确添加shared_preferences依赖
- API使用:遵循库的设计规范,合理封装业务逻辑
- 平台适配:关注OpenHarmony特有的存储机制和权限要求
- 性能优化:根据实际场景选择合适的数据存储策略
8.2 进阶扩展方向
对于更复杂的数据持久化需求,可以考虑以下方案:
- SQLite:使用sqflite或drift库处理结构化数据
- 文件存储:直接读写文件,适合大型二进制数据
- 对象存储:使用hive或isar等NoSQL数据库,支持对象直接持久化
- 加密存储:对于敏感数据,考虑使用flutter_secure_storage等加密存储方案
shared_preferences虽然简单,但却是Flutter应用开发中不可或缺的工具。结合OpenHarmony平台的特性合理使用,可以为用户提供流畅且稳定的使用体验。希望本文能为正在进行鸿蒙化适配的开发者提供有价值的参考。
本文仓库地址:https://atomgit.com/your_username/flutter_shared_preferences_oh
参考文献:
- OpenHarmony 官方文档:https://gitee.com/openharmony/docs
- shared_preferences 官方文档:https://pub.dev/packages/shared_preferences
- Flutter for OpenHarmony 文档:https://gitee.com/openharmony-sig/flutter
更多推荐




所有评论(0)