使用已经完成鸿蒙化适配的Flutter本地持久化存储三方库shared_preferences让你的应用能够保存用户偏好设置、缓存数据等
本文介绍了如何在HarmonyOS(OpenHarmony)上使用Flutter的shared_preferences插件实现本地数据持久化存储。主要内容包括: 引入适配后的shared_preferences库,通过修改pubspec.yaml文件添加Git依赖 解释shared_preferences的作用和优势,它是一个跨平台的键值对存储系统,适合保存用户偏好设置等简单数据 。
你好!👋 欢迎来到这篇关于 shared_preferences 在 HarmonyOS (OpenHarmony) 上使用的实战教程。当前已经完成鸿蒙化适配的Flutter三方库均可在flutter_packages仓库下查看。
本文将手把手带你实现本地数据持久化存储,让你的应用能够保存用户偏好设置、缓存数据等!📦
📦 1. 引入依赖:pubspec.yaml
首先,我们需要在项目的配置文件中引入适配后的 shared_preferences 库。
修改文件:pubspec.yaml
操作:在 dependencies 节点下添加 shared_preferences 的 git 依赖配置。
dependencies:
flutter:
sdk: flutter
# 👇 新增:引入 shared_preferences 鸿蒙适配版本
shared_preferences:
git:
url: https://gitcode.com/openharmony-tpc/flutter_packages.git
path: packages/shared_preferences/shared_preferences
ref: br_shared_preferences-v2.5.3_ohos
💡 提示:修改完成后,别忘了运行终端命令
flutter pub get来下载依赖!
🎯 为什么需要 shared_preferences?
shared_preferences 是 Flutter 官方提供的一个插件,用于简单数据的本地持久化存储。
想象一下,你的应用需要:
- 🔐 记住用户的登录状态
- 🌙 保存用户的主题偏好(深色/浅色模式)
- 🌍 存储用户选择的语言设置
- 📊 缓存一些简单的配置数据
- 🎯 保存用户的使用习惯
传统方式需要为每个平台编写不同的存储代码。而 shared_preferences 就像一个 🗄️ 跨平台小型数据库,一个方法搞定所有平台!
核心优势:
- ✅ 跨平台统一:一套代码适配多个平台
- ✅ 简单易用:键值对存储,API 简洁
- ✅ 类型安全:支持多种数据类型
- ✅ 异步操作:不阻塞主线程
- ✅ 持久化存储:应用重启后数据仍然保留
👶 新手小课堂:shared_preferences 是什么?
shared_preferences 可以理解为一个 🗃️ 简单的键值对存储系统。
就像一个 📒 小笔记本:
- 你可以在上面写下 “用户名 = 张三”
- 下次打开笔记本,“张三” 还在那里
- 你也可以修改或删除这些记录
它与其他存储方式的区别:
| 存储方式 | 适用场景 | 数据量 | 复杂度 |
|---|---|---|---|
| 💾 shared_preferences | 简单配置、偏好设置 | 小 | 低 |
| 📁 文件存储 | 大文件、日志 | 大 | 中 |
| 🗄️ 数据库 (SQLite) | 结构化数据、关系查询 | 大 | 高 |
支持的数据类型:
- 📝 String:字符串(如用户名、配置项)
- 🔢 int:整数(如年龄、计数器)
- 📊 double:浮点数(如分数、价格)
- ✅ bool:布尔值(如开关状态)
- 📋 List<String>:字符串列表(如标签、历史记录)
🛠️ 2. 二次封装:lib/services/shared_preferences_service.dart
为了提高代码复用性和可维护性,我们创建一个服务类对 shared_preferences 进行二次封装。
2.1 为什么需要二次封装?
问题场景:
- ❌ 每次使用都要获取 SharedPreferences 实例
- ❌ 异常处理代码重复
- ❌ 缺少统一的日志记录
- ❌ 键名管理混乱
二次封装的好处:
- ✅ 代码复用:封装常用功能,避免重复编写
- ✅ 统一管理:集中处理初始化、错误、日志
- ✅ 易于维护:修改只需在一处进行
- ✅ 类型安全:提供类型明确的 API
2.2 创建服务类
新建文件:lib/services/shared_preferences_service.dart
📊 SharedPreferences 服务架构流程:
核心代码实现:
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/foundation.dart';
/// 💾 本地存储服务类
/// 对 shared_preferences 进行二次封装,提供统一的本地存储接口
class SharedPreferencesService {
// 单例模式
static final SharedPreferencesService _instance = SharedPreferencesService._internal();
factory SharedPreferencesService() => _instance;
SharedPreferencesService._internal();
SharedPreferences? _prefs;
/// 🔧 初始化 SharedPreferences
Future<void> init() async {
_prefs ??= await SharedPreferences.getInstance();
}
/// 🔍 获取 SharedPreferences 实例
Future<SharedPreferences> get prefs async {
await init();
return _prefs!;
}
// ==================== String 类型 ====================
/// 📝 保存字符串
Future<bool> setString(String key, String value) async {
try {
await init();
return await _prefs!.setString(key, value);
} catch (e) {
if (kDebugMode) {
print('📝 保存字符串错误: $e');
}
return false;
}
}
/// 📖 读取字符串
Future<String?> getString(String key) async {
try {
await init();
return _prefs!.getString(key);
} catch (e) {
if (kDebugMode) {
print('📖 读取字符串错误: $e');
}
return null;
}
}
// ==================== int 类型 ====================
/// 🔢 保存整数
Future<bool> setInt(String key, int value) async {
try {
await init();
return await _prefs!.setInt(key, value);
} catch (e) {
if (kDebugMode) {
print('🔢 保存整数错误: $e');
}
return false;
}
}
/// 🔢 读取整数
Future<int?> getInt(String key) async {
try {
await init();
return _prefs!.getInt(key);
} catch (e) {
if (kDebugMode) {
print('🔢 读取整数错误: $e');
}
return null;
}
}
// ==================== double 类型 ====================
/// 📊 保存浮点数
Future<bool> setDouble(String key, double value) async {
try {
await init();
return await _prefs!.setDouble(key, value);
} catch (e) {
if (kDebugMode) {
print('📊 保存浮点数错误: $e');
}
return false;
}
}
/// 📊 读取浮点数
Future<double?> getDouble(String key) async {
try {
await init();
return _prefs!.getDouble(key);
} catch (e) {
if (kDebugMode) {
print('📊 读取浮点数错误: $e');
}
return null;
}
}
// ==================== bool 类型 ====================
/// ✅ 保存布尔值
Future<bool> setBool(String key, bool value) async {
try {
await init();
return await _prefs!.setBool(key, value);
} catch (e) {
if (kDebugMode) {
print('✅ 保存布尔值错误: $e');
}
return false;
}
}
/// ✅ 读取布尔值
Future<bool?> getBool(String key) async {
try {
await init();
return _prefs!.getBool(key);
} catch (e) {
if (kDebugMode) {
print('✅ 读取布尔值错误: $e');
}
return null;
}
}
// ==================== List<String> 类型 ====================
/// 📋 保存字符串列表
Future<bool> setStringList(String key, List<String> value) async {
try {
await init();
return await _prefs!.setStringList(key, value);
} catch (e) {
if (kDebugMode) {
print('📋 保存字符串列表错误: $e');
}
return false;
}
}
/// 📋 读取字符串列表
Future<List<String>?> getStringList(String key) async {
try {
await init();
return _prefs!.getStringList(key);
} catch (e) {
if (kDebugMode) {
print('📋 读取字符串列表错误: $e');
}
return null;
}
}
// ==================== 通用操作 ====================
/// 🗑️ 删除指定键
Future<bool> remove(String key) async {
try {
await init();
return await _prefs!.remove(key);
} catch (e) {
if (kDebugMode) {
print('🗑️ 删除键错误: $e');
}
return false;
}
}
/// 🧹 清空所有数据
Future<bool> clear() async {
try {
await init();
return await _prefs!.clear();
} catch (e) {
if (kDebugMode) {
print('🧹 清空数据错误: $e');
}
return false;
}
}
/// 🔍 检查键是否存在
Future<bool> containsKey(String key) async {
try {
await init();
return _prefs!.containsKey(key);
} catch (e) {
if (kDebugMode) {
print('🔍 检查键错误: $e');
}
return false;
}
}
/// 📊 获取所有键
Future<Set<String>> getKeys() async {
try {
await init();
return _prefs!.getKeys();
} catch (e) {
if (kDebugMode) {
print('📊 获取所有键错误: $e');
}
return {};
}
}
}

👶 新手小课堂:为什么使用单例模式?
你可能注意到了这段代码:
static final SharedPreferencesService _instance = SharedPreferencesService._internal();
factory SharedPreferencesService() => _instance;
SharedPreferencesService._internal();
这是 单例模式(Singleton Pattern) 的实现。
为什么 SharedPreferences 需要单例?
- 💾 资源共享:SharedPreferences 实例获取是异步的,创建多个实例浪费资源
- 🔄 状态一致:确保整个应用使用同一个存储实例
- ⚡ 性能优化:避免重复初始化,提高访问速度
- 🎯 管理方便:集中管理所有存储操作
✨ 单例模式的优势:
- 💾 节省内存:避免重复创建对象
- ⚡ 提高性能:只初始化一次
- 🎯 统一管理:全局共享同一实例
- 🔒 线程安全:Dart 的单例实现天然线程安全
💻 3. 使用方法:lib/shared_preferences_demo.dart
接下来,我们创建一个完整的演示页面,展示如何使用封装好的服务类。
新建文件:lib/shared_preferences_demo.dart
3.1 页面功能概览
我们的演示页面将实现以下功能:
- 📝 字符串存取:保存和读取用户名等文本
- 🔢 整数存取:保存和读取年龄、计数器等
- 📊 浮点数存取:保存和读取分数、价格等
- ✅ 布尔值存取:保存和读取开关状态
- 📋 列表存取:保存和读取标签、历史记录等
- 💾 数据展示:查看所有已存储的数据
- 🗑️ 数据管理:删除单条或清空所有数据
3.2 核心代码实现
导入依赖:
import 'package:flutter/material.dart';
import 'services/shared_preferences_service.dart';
初始化服务:
final SharedPreferencesService _prefsService = SharedPreferencesService();
void initState() {
super.initState();
_initPrefs();
}
Future<void> _initPrefs() async {
await _prefsService.init();
_loadAllData(); // 加载已存储的数据
}
保存字符串示例:
Future<void> _saveString() async {
final key = _stringKeyController.text.trim();
final value = _stringValueController.text.trim();
if (key.isEmpty || value.isEmpty) {
_showMessage('请输入键和值', isError: true);
return;
}
setState(() => _isLoading = true);
final success = await _prefsService.setString(key, value);
_showMessage(success ? '✅ 字符串保存成功' : '❌ 保存失败', isError: !success);
await _loadAllData(); // 刷新数据列表
}

读取字符串示例:
Future<void> _readString() async {
final key = _stringKeyController.text.trim();
if (key.isEmpty) {
_showMessage('请输入键名', isError: true);
return;
}
setState(() => _isLoading = true);
final value = await _prefsService.getString(key);
_showMessage(value != null ? '📖 读取成功: $value' : '⚠️ 键不存在', isError: value == null);
setState(() => _isLoading = false);
}

保存布尔值示例:
Future<void> _saveBool() async {
final key = _boolKeyController.text.trim();
if (key.isEmpty) {
_showMessage('请输入键名', isError: true);
return;
}
setState(() => _isLoading = true);
final success = await _prefsService.setBool(key, _boolValue);
_showMessage(success ? '✅ 布尔值保存成功' : '❌ 保存失败', isError: !success);
await _loadAllData();
}

保存列表示例:
Future<void> _saveList() async {
final key = _listKeyController.text.trim();
final valueStr = _listValueController.text.trim();
if (key.isEmpty || valueStr.isEmpty) {
_showMessage('请输入键和值', isError: true);
return;
}
// 将逗号分隔的字符串转为列表
final list = valueStr.split(',').map((e) => e.trim()).toList();
setState(() => _isLoading = true);
final success = await _prefsService.setStringList(key, list);
_showMessage(success ? '✅ 列表保存成功' : '❌ 保存失败', isError: !success);
await _loadAllData();
}

清空所有数据:
Future<void> _clearAll() async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('⚠️ 确认清空'),
content: const Text('确定要清空所有存储的数据吗?此操作不可恢复!'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('取消'),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
style: TextButton.styleFrom(foregroundColor: Colors.red),
child: const Text('确认清空'),
),
],
),
);
if (confirmed == true) {
setState(() => _isLoading = true);
final success = await _prefsService.clear();
_showMessage(success ? '🧹 数据已清空' : '❌ 清空失败', isError: !success);
await _loadAllData();
}
}

👶 新手小课堂:异步操作与 async/await
你可能注意到代码中大量使用了 async 和 await:
Future<void> _saveString() async {
final success = await _prefsService.setString(key, value);
}
为什么需要异步?
SharedPreferences 的读写操作涉及到磁盘 I/O,如果同步执行会阻塞主线程,导致界面卡顿。
异步操作的好处:
- ⚡ 不阻塞 UI:用户界面保持响应
- 🔄 并发执行:可以同时执行多个操作
- 🎯 代码清晰:async/await 使异步代码像同步一样易读
使用模式:
// ❌ 错误:不等待异步操作
void saveData() {
_prefsService.setString('key', 'value'); // 可能还没保存完就继续了
print('已保存'); // 实际可能还没保存
}
// ✅ 正确:等待异步操作完成
Future<void> saveData() async {
await _prefsService.setString('key', 'value'); // 等待保存完成
print('已保存'); // 确保已经保存
}
🚀 4. 配置应用入口:lib/main.dart
最后,我们在应用的主页添加入口,跳转到演示页面。
修改文件:lib/main.dart
操作:
- 导入演示页面文件
- 在按钮列表中添加跳转按钮
// 1. 导入头文件
import 'shared_preferences_demo.dart';
// 2. 在 build 方法中添加跳转按钮
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SharedPreferencesDemoPage(),
),
);
},
child: const Text('Go to SharedPreferences Demo'),
),
⚠️ 5. 常见错误与解决方案
❌ 错误 1:初始化失败
😱 错误现象:
- 调用
SharedPreferences.getInstance()卡住 - 抛出
MissingPluginException异常
🔍 原因分析:
- ❌ 依赖未正确安装
- ❌ 原生插件未注册
- ❌ Flutter 绑定未初始化
✅ 解决方案:
1. 确保依赖已安装:
flutter pub get
2. 在 main() 中确保初始化:
void main() async {
// 👇 确保 Flutter 绑定初始化
WidgetsFlutterBinding.ensureInitialized();
// 可选:预先初始化 SharedPreferences
await SharedPreferences.getInstance();
runApp(const MyApp());
}
3. 清理并重新构建:
flutter clean
flutter pub get
flutter run
❌ 错误 2:类型不匹配
😱 错误现象:
- 保存的是 int,读取时用 getString,返回 null
- 保存的是 String,读取时用 getInt,返回 null
🔍 原因分析:
- ❌ 保存和读取使用了不同的类型方法
- ❌ 键名拼写错误
✅ 解决方案:
1. 保持类型一致:
// ❌ 错误:类型不匹配
await prefs.setInt('age', 25);
final age = prefs.getString('age'); // 返回 null
// ✅ 正确:类型一致
await prefs.setInt('age', 25);
final age = prefs.getInt('age'); // 返回 25
2. 使用常量管理键名:
// 定义键名常量,避免拼写错误
class PrefsKeys {
static const String username = 'username';
static const String age = 'age';
static const String isVip = 'isVip';
}
// 使用常量
await prefs.setString(PrefsKeys.username, '张三');
final name = prefs.getString(PrefsKeys.username);
❌ 错误 3:数据未持久化
😱 错误现象:
- 保存成功,但重启应用后数据丢失
- 热重载后数据消失
🔍 原因分析:
- ❌ 保存操作未完成就退出
- ❌ 使用了错误的存储路径
- ❌ 鸿蒙系统权限问题
✅ 解决方案:
1. 确保保存操作完成:
// ❌ 错误:没有等待保存完成
void onExit() {
prefs.setString('key', 'value'); // 可能还没保存就退出了
}
// ✅ 正确:等待保存完成
Future<void> onExit() async {
await prefs.setString('key', 'value'); // 确保保存完成
}
2. 检查返回值:
final success = await prefs.setString('key', 'value');
if (!success) {
print('保存失败,请检查存储权限');
}
❌ 错误 4:存储大量数据导致卡顿
😱 错误现象:
- 保存大量数据时界面卡顿
- 读取数据时响应缓慢
🔍 原因分析:
- ❌ SharedPreferences 不适合存储大量数据
- ❌ 频繁读写操作
✅ 解决方案:
1. 合理使用存储:
| 数据类型 | 推荐存储方式 |
|---|---|
| 简单配置(<100KB) | ✅ SharedPreferences |
| 大量结构化数据 | 🗄️ SQLite / Hive |
| 大文件 | 📁 文件存储 |
2. 批量操作优化:
// ❌ 错误:多次调用
await prefs.setString('key1', 'value1');
await prefs.setString('key2', 'value2');
await prefs.setString('key3', 'value3');
// ✅ 正确:一次性操作(如需批量存储,考虑用 Map 转 JSON)
final data = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'};
await prefs.setString('batchData', jsonEncode(data));
❌ 错误 5:键名冲突
😱 错误现象:
- 不同功能模块使用了相同的键名
- 数据被意外覆盖
🔍 原因分析:
- ❌ 没有统一管理键名
- ❌ 键名命名不规范
✅ 解决方案:
使用命名空间或前缀:
// 定义键名常量类,使用前缀区分模块
class PrefsKeys {
// 用户模块
static const String userUsername = 'user_username';
static const String userAge = 'user_age';
static const String userIsVip = 'user_isVip';
// 设置模块
static const String settingTheme = 'setting_theme';
static const String settingLanguage = 'setting_language';
// 缓存模块
static const String cacheLastUpdate = 'cache_lastUpdate';
static const String cacheTags = 'cache_tags';
}
📋 6. 数据类型与用途对比表
| 数据类型 | 方法 | 适用场景 | 示例 |
|---|---|---|---|
| 📝 String | setString/getString | 文本配置 | 用户名、Token、配置项 |
| 🔢 int | setInt/getInt | 整数值 | 年龄、计数器、页码 |
| 📊 double | setDouble/getDouble | 浮点数 | 分数、价格、进度 |
| ✅ bool | setBool/getBool | 开关状态 | 是否登录、是否VIP、是否首次启动 |
| 📋 List<String> | setStringList/getStringList | 字符串数组 | 标签、搜索历史、收藏列表 |
使用场景建议:
- 🔐 登录状态 → 使用 bool
- 👤 用户信息 → 使用 String
- 🎯 计数器 → 使用 int
- 💰 金额 → 使用 double
- 🏷️ 标签列表 → 使用 List<String>
🎨 7. 最佳实践建议
✨ 键名管理
1. 使用常量类管理所有键名:
/// 📝 SharedPreferences 键名常量
class PrefsKeys {
PrefsKeys._(); // 私有构造函数,防止实例化
// 用户相关
static const String username = 'pref_username';
static const String userId = 'pref_user_id';
static const String isLoggedIn = 'pref_is_logged_in';
// 设置相关
static const String themeMode = 'pref_theme_mode';
static const String language = 'pref_language';
static const String fontSize = 'pref_font_size';
// 缓存相关
static const String lastSyncTime = 'pref_last_sync_time';
static const String cacheVersion = 'pref_cache_version';
}
🔒 数据安全
1. 敏感数据不要明文存储:
// ❌ 错误:明文存储密码
await prefs.setString('password', '123456');
// ✅ 正确:敏感数据使用 flutter_secure_storage
// 或至少进行加密处理
import 'dart:convert';
import 'package:crypto/crypto.dart';
String hashPassword(String password) {
final bytes = utf8.encode(password);
return sha256.convert(bytes).toString();
}
await prefs.setString('passwordHash', hashPassword('123456'));
⚡ 性能优化
1. 避免频繁读写:
// ❌ 错误:每次都读取
Widget build(BuildContext context) {
final username = prefs.getString('username'); // 每次 build 都读取
return Text(username ?? '');
}
// ✅ 正确:缓存到变量
String? _username;
void initState() {
super.initState();
_loadUsername();
}
Future<void> _loadUsername() async {
final username = await _prefsService.getString('username');
setState(() => _username = username);
}
Widget build(BuildContext context) {
return Text(_username ?? ''); // 使用缓存的值
}
2. 批量数据使用 JSON:
// 存储复杂对象
final user = {'name': '张三', 'age': 25, 'tags': ['Flutter', '鸿蒙']};
await prefs.setString('user', jsonEncode(user));
// 读取复杂对象
final userJson = prefs.getString('user');
if (userJson != null) {
final user = jsonDecode(userJson) as Map<String, dynamic>;
}
🎉 结语
通过以上步骤,我们完成了鸿蒙化Flutter三方库 shared_preferences 在鸿蒙系统上的完整集成!🚀
📚 完整存储流程:
🌟 核心API总结:
| API | 用途 | 返回值 | 说明 |
|---|---|---|---|
getInstance() |
获取实例 | Future | 异步获取单例实例 |
setXxx() |
保存数据 | Future | 返回操作是否成功 |
getXxx() |
读取数据 | T? | 返回值或 null |
remove() |
删除键 | Future | 删除指定键值对 |
clear() |
清空数据 | Future | 清空所有数据 |
containsKey() |
检查键 | bool | 检查键是否存在 |
getKeys() |
获取所有键 | Set | 返回所有键名集合 |
快去运行你的鸿蒙应用试试吧!Happy Coding! 💻⚡️
更多推荐


所有评论(0)