目录
概述
本章节主要详细介绍在使用跨平台框架 Flutter 开发鸿蒙应用程序时,如何引入和使用鸿蒙化适配的 shared_preferences 库进行本地数据持久化存储。shared_preferences 是 Flutter 提供的本地键值存储工具,已经完成鸿蒙化适配工作,可在flutter_packages查看适配情况。
🎯 本教程目标
通过本教程,你将学会:

📁 项目文件结构
在开始之前,让我们先了解一下项目结构:
lib/
├── utils/ # 工具类目录
│ └── preferences_helper.dart # 🔧 SharedPreferences 二次封装工具类
└── screens/ # 页面文件目录
├── home_page.dart # 🏠 首页(使用 SharedPreferences 保存当前城市)
└── city_manage_page.dart # 🏙️ 城市管理页(使用 SharedPreferences 保存城市列表)
🎯 本教程将创建的文件(按顺序)
严格按照以下顺序创建文件,每个文件创建后立即验证:
🛠️ 技术栈
- SharedPreferences: Flutter 本地键值存储工具
- 鸿蒙适配版本: 从 OpenHarmony TPC 仓库引入的适配版本
- 错误处理: MissingPluginException 异常处理
💡 SharedPreferences 简介
shared_preferences 是 Flutter 提供的本地键值存储工具,具有以下特点:
- 📦 轻量级: 适合存储简单的配置信息
- 💾 持久化: 数据会持久化保存在设备上,应用重启后仍然存在
- 🔒 类型安全: 支持多种数据类型(String、int、bool、double、List)
- ⚡ 异步操作: 所有操作都是异步的,不会阻塞 UI 线程
- 🌐 跨平台: 支持 Android、iOS、Web、Windows、macOS、Linux 和 HarmonyOS
引入三方库步骤
📋 流程图概览
#mermaid-svg-10KHFFMOgpk91fFV{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-10KHFFMOgpk91fFV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-10KHFFMOgpk91fFV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-10KHFFMOgpk91fFV .error-icon{fill:#552222;}#mermaid-svg-10KHFFMOgpk91fFV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-10KHFFMOgpk91fFV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-10KHFFMOgpk91fFV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-10KHFFMOgpk91fFV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-10KHFFMOgpk91fFV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-10KHFFMOgpk91fFV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-10KHFFMOgpk91fFV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-10KHFFMOgpk91fFV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-10KHFFMOgpk91fFV .marker.cross{stroke:#333333;}#mermaid-svg-10KHFFMOgpk91fFV svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-10KHFFMOgpk91fFV p{margin:0;}#mermaid-svg-10KHFFMOgpk91fFV .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-10KHFFMOgpk91fFV .cluster-label text{fill:#333;}#mermaid-svg-10KHFFMOgpk91fFV .cluster-label span{color:#333;}#mermaid-svg-10KHFFMOgpk91fFV .cluster-label span p{background-color:transparent;}#mermaid-svg-10KHFFMOgpk91fFV .label text,#mermaid-svg-10KHFFMOgpk91fFV span{fill:#333;color:#333;}#mermaid-svg-10KHFFMOgpk91fFV .node rect,#mermaid-svg-10KHFFMOgpk91fFV .node circle,#mermaid-svg-10KHFFMOgpk91fFV .node ellipse,#mermaid-svg-10KHFFMOgpk91fFV .node polygon,#mermaid-svg-10KHFFMOgpk91fFV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-10KHFFMOgpk91fFV .rough-node .label text,#mermaid-svg-10KHFFMOgpk91fFV .node .label text,#mermaid-svg-10KHFFMOgpk91fFV .image-shape .label,#mermaid-svg-10KHFFMOgpk91fFV .icon-shape .label{text-anchor:middle;}#mermaid-svg-10KHFFMOgpk91fFV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-10KHFFMOgpk91fFV .rough-node .label,#mermaid-svg-10KHFFMOgpk91fFV .node .label,#mermaid-svg-10KHFFMOgpk91fFV .image-shape .label,#mermaid-svg-10KHFFMOgpk91fFV .icon-shape .label{text-align:center;}#mermaid-svg-10KHFFMOgpk91fFV .node.clickable{cursor:pointer;}#mermaid-svg-10KHFFMOgpk91fFV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-10KHFFMOgpk91fFV .arrowheadPath{fill:#333333;}#mermaid-svg-10KHFFMOgpk91fFV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-10KHFFMOgpk91fFV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-10KHFFMOgpk91fFV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-10KHFFMOgpk91fFV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-10KHFFMOgpk91fFV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-10KHFFMOgpk91fFV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-10KHFFMOgpk91fFV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-10KHFFMOgpk91fFV .cluster text{fill:#333;}#mermaid-svg-10KHFFMOgpk91fFV .cluster span{color:#333;}#mermaid-svg-10KHFFMOgpk91fFV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-10KHFFMOgpk91fFV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-10KHFFMOgpk91fFV rect.text{fill:none;stroke-width:0;}#mermaid-svg-10KHFFMOgpk91fFV .icon-shape,#mermaid-svg-10KHFFMOgpk91fFV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-10KHFFMOgpk91fFV .icon-shape p,#mermaid-svg-10KHFFMOgpk91fFV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-10KHFFMOgpk91fFV .icon-shape rect,#mermaid-svg-10KHFFMOgpk91fFV .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-10KHFFMOgpk91fFV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-10KHFFMOgpk91fFV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-10KHFFMOgpk91fFV :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
是
否
📝 开始引入三方库
📄 步骤1: 打开 pubspec.yaml 文件
📝 步骤2: 添加 Git 依赖配置
💾 步骤3: 保存文件
⬇️ 步骤4: 运行 flutter pub get
✅ 安装成功?
🔍 步骤5: 验证安装
❌ 检查网络连接和 Git 配置
网络正常?
🌐 检查网络连接
🎉 完成引入
📝 步骤 1:打开 pubspec.yaml 文件
文件路径: pubspec.yaml(项目根目录)
操作说明:
📝 步骤 2:添加 Git 依赖配置
位置: pubspec.yaml 文件的 dependencies: 部分
操作步骤:
dependencies:
flutter:
sdk: flutter
# SharedPreferences 鸿蒙适配版本(使用 Git 方式引入)
shared_preferences:
git:
url: "https://gitcode.com/openharmony-tpc/flutter_packages.git"
path: "packages/shared_preferences/shared_preferences"
📝 代码解读:
- shared_preferences:: 依赖包名称
- git:: 指定使用 Git 方式引入依赖
- url:: Git 仓库地址(OpenHarmony TPC 官方仓库)
- path:: 包在仓库中的路径
⚠️ 注意事项:
- ✅ 必须使用 Git 方式引入,不能使用 pub.dev 版本
- ✅ 确保网络连接正常,能够访问 gitcode.com
- ✅ 缩进必须正确(使用2个空格)
验证:
- ✅ 确认缩进正确(使用2个空格)
- ✅ 确认 URL 和 path 正确
- ✅ 确认没有语法错误(冒号、引号等)
📝 步骤 3:保存文件
操作说明:
📝 步骤 4:运行 flutter pub get
操作步骤:
flutter pub get
预期输出:
Resolving dependencies…
Downloading packages…
+ shared_preferences (from git)
Got dependencies!
✅ 成功标志:
- 看到 Got dependencies! 提示
- 没有错误信息
- .dart_tool/package_config.json 文件中包含 shared_preferences
📝 步骤 5:验证安装
操作步骤:
import 'package:shared_preferences/shared_preferences.dart';
✅ 验证成功标志:
- IDE 没有报错
- 可以正常导入包
- 代码提示正常工作
二次封装实现
📋 封装流程图
#mermaid-svg-Z5siEUFQg08PZqEc{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Z5siEUFQg08PZqEc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Z5siEUFQg08PZqEc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Z5siEUFQg08PZqEc .error-icon{fill:#552222;}#mermaid-svg-Z5siEUFQg08PZqEc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Z5siEUFQg08PZqEc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Z5siEUFQg08PZqEc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Z5siEUFQg08PZqEc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Z5siEUFQg08PZqEc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Z5siEUFQg08PZqEc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Z5siEUFQg08PZqEc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Z5siEUFQg08PZqEc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Z5siEUFQg08PZqEc .marker.cross{stroke:#333333;}#mermaid-svg-Z5siEUFQg08PZqEc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Z5siEUFQg08PZqEc p{margin:0;}#mermaid-svg-Z5siEUFQg08PZqEc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Z5siEUFQg08PZqEc .cluster-label text{fill:#333;}#mermaid-svg-Z5siEUFQg08PZqEc .cluster-label span{color:#333;}#mermaid-svg-Z5siEUFQg08PZqEc .cluster-label span p{background-color:transparent;}#mermaid-svg-Z5siEUFQg08PZqEc .label text,#mermaid-svg-Z5siEUFQg08PZqEc span{fill:#333;color:#333;}#mermaid-svg-Z5siEUFQg08PZqEc .node rect,#mermaid-svg-Z5siEUFQg08PZqEc .node circle,#mermaid-svg-Z5siEUFQg08PZqEc .node ellipse,#mermaid-svg-Z5siEUFQg08PZqEc .node polygon,#mermaid-svg-Z5siEUFQg08PZqEc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Z5siEUFQg08PZqEc .rough-node .label text,#mermaid-svg-Z5siEUFQg08PZqEc .node .label text,#mermaid-svg-Z5siEUFQg08PZqEc .image-shape .label,#mermaid-svg-Z5siEUFQg08PZqEc .icon-shape .label{text-anchor:middle;}#mermaid-svg-Z5siEUFQg08PZqEc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Z5siEUFQg08PZqEc .rough-node .label,#mermaid-svg-Z5siEUFQg08PZqEc .node .label,#mermaid-svg-Z5siEUFQg08PZqEc .image-shape .label,#mermaid-svg-Z5siEUFQg08PZqEc .icon-shape .label{text-align:center;}#mermaid-svg-Z5siEUFQg08PZqEc .node.clickable{cursor:pointer;}#mermaid-svg-Z5siEUFQg08PZqEc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Z5siEUFQg08PZqEc .arrowheadPath{fill:#333333;}#mermaid-svg-Z5siEUFQg08PZqEc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Z5siEUFQg08PZqEc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Z5siEUFQg08PZqEc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Z5siEUFQg08PZqEc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Z5siEUFQg08PZqEc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Z5siEUFQg08PZqEc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Z5siEUFQg08PZqEc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Z5siEUFQg08PZqEc .cluster text{fill:#333;}#mermaid-svg-Z5siEUFQg08PZqEc .cluster span{color:#333;}#mermaid-svg-Z5siEUFQg08PZqEc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Z5siEUFQg08PZqEc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Z5siEUFQg08PZqEc rect.text{fill:none;stroke-width:0;}#mermaid-svg-Z5siEUFQg08PZqEc .icon-shape,#mermaid-svg-Z5siEUFQg08PZqEc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Z5siEUFQg08PZqEc .icon-shape p,#mermaid-svg-Z5siEUFQg08PZqEc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Z5siEUFQg08PZqEc .icon-shape rect,#mermaid-svg-Z5siEUFQg08PZqEc .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Z5siEUFQg08PZqEc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Z5siEUFQg08PZqEc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Z5siEUFQg08PZqEc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
🎯 开始二次封装
📝 创建 PreferencesHelper 类
🔑 定义常量键名
📥 实现读取方法
💾 实现保存方法
🗑️ 实现删除方法
🔄 实现更新方法
⚠️ 添加错误处理
✅ 完成封装
🎯 为什么需要二次封装?
直接使用 SharedPreferences 存在以下问题:
二次封装的优点:
📝 步骤 1:创建 PreferencesHelper 工具类
文件路径: lib/utils/preferences_helper.dart
操作步骤:
// 导入 Flutter 基础库,用于 debugPrint
import 'package:flutter/foundation.dart';
// 导入 Flutter 服务,用于 MissingPluginException
import 'package:flutter/services.dart';
// 导入 shared_preferences 三方库,用于本地数据存储
import 'package:shared_preferences/shared_preferences.dart';
/// 首选项管理工具类
/// 用于管理应用的本地存储数据,如首次使用标志等
class PreferencesHelper {
/// 首次使用标志的键名
/// 用于在 SharedPreferences 中存储和读取是否首次使用应用的标志
static const String _keyIsFirstLaunch = 'is_first_launch';
/// 检查是否首次启动应用
///
/// 返回值:
/// – true: 首次启动,需要显示引导页
/// – false: 非首次启动,直接进入主页面
///
/// 使用示例:
/// ```dart
/// bool isFirstLaunch = await PreferencesHelper.isFirstLaunch();
/// if (isFirstLaunch) {
/// // 显示引导页
/// } else {
/// // 直接进入主页面
/// }
/// ```
static Future<bool> isFirstLaunch() async {
try {
// 获取 SharedPreferences 实例
// SharedPreferences 是 Flutter 提供的本地键值存储工具
// 数据会持久化保存在设备上,应用重启后仍然存在
final prefs = await SharedPreferences.getInstance();
// 读取首次启动标志
// getBool: 获取布尔值,如果键不存在则返回 null
// ?? true: 如果返回 null(即键不存在),则默认为 true(首次启动)
final isFirst = prefs.getBool(_keyIsFirstLaunch) ?? true;
return isFirst;
} on MissingPluginException catch (e) {
// 处理插件未注册错误
// 这通常发生在热重载后,需要完全重启应用
debugPrint('SharedPreferences 插件未注册: $e');
debugPrint('提示:请完全停止应用后重新运行(不要使用热重载)');
// 默认返回 true,显示引导页
return true;
} catch (e) {
// 如果读取失败(例如权限问题、存储空间不足等),默认返回 true
// 这样即使出错,用户也能看到引导页
// 可以在这里添加日志记录错误信息(可选)
debugPrint('读取首次启动标志失败: $e');
return true;
}
}
/// 设置已非首次启动
///
/// 当用户完成引导页或点击跳过时调用此方法
/// 将首次启动标志设置为 false,下次启动时不再显示引导页
///
/// 使用示例:
/// ```dart
/// await PreferencesHelper.setNotFirstLaunch();
/// ```
static Future<void> setNotFirstLaunch() async {
try {
// 获取 SharedPreferences 实例
final prefs = await SharedPreferences.getInstance();
// 设置首次启动标志为 false
// setBool: 保存布尔值到本地存储
// await: 等待保存操作完成,确保数据已写入
await prefs.setBool(_keyIsFirstLaunch, false);
} on MissingPluginException catch (e) {
// 处理插件未注册错误
debugPrint('SharedPreferences 插件未注册,无法保存: $e');
debugPrint('提示:请完全停止应用后重新运行(不要使用热重载)');
// 静默失败,不影响应用运行
} catch (e) {
// 如果保存失败,记录错误信息
// 这里不抛出异常,避免影响应用正常运行
debugPrint('保存首次启动标志失败: $e');
}
}
/// 重置首次启动标志(用于测试或重新显示引导页)
///
/// 将首次启动标志重置为 true,下次启动时会再次显示引导页
///
/// 使用示例:
/// ```dart
/// await PreferencesHelper.resetFirstLaunch();
/// ```
static Future<void> resetFirstLaunch() async {
try {
final prefs = await SharedPreferences.getInstance();
// 删除首次启动标志,或设置为 true
await prefs.setBool(_keyIsFirstLaunch, true);
// 或者删除键:await prefs.remove(_keyIsFirstLaunch);
} on MissingPluginException catch (e) {
// 处理插件未注册错误
debugPrint('SharedPreferences 插件未注册,无法重置: $e');
debugPrint('提示:请完全停止应用后重新运行(不要使用热重载)');
} catch (e) {
debugPrint('重置首次启动标志失败: $e');
}
}
}
📝 代码解读:
1. 导入必要的库:
- package:flutter/foundation.dart: 提供 debugPrint 用于调试输出
- package:flutter/services.dart: 提供 MissingPluginException 异常类型
- package:shared_preferences/shared_preferences.dart: SharedPreferences 库
2. 常量键名管理:
- _keyIsFirstLaunch: 使用私有常量管理键名,避免拼写错误
3. 错误处理策略:
- MissingPluginException: 专门处理插件未注册错误(热重载常见问题)
- catch (e): 处理其他未知错误
- 默认值策略:读取失败时返回安全默认值
4. 方法设计:
- static: 使用静态方法,无需实例化
- Future: 异步操作,不阻塞 UI
- 返回值:提供明确的返回值类型
📝 步骤 2:扩展 PreferencesHelper(可选)
如果需要存储更多类型的数据,可以扩展 PreferencesHelper:
/// 扩展 PreferencesHelper,添加更多通用方法
extension PreferencesHelperExtension on PreferencesHelper {
/// 保存字符串
static Future<bool> saveString(String key, String value) async {
try {
final prefs = await SharedPreferences.getInstance();
return await prefs.setString(key, value);
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
return false;
} catch (e) {
debugPrint('保存字符串失败: $e');
return false;
}
}
/// 读取字符串
static Future<String?> getString(String key) async {
try {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
return null;
} catch (e) {
debugPrint('读取字符串失败: $e');
return null;
}
}
/// 保存整数
static Future<bool> saveInt(String key, int value) async {
try {
final prefs = await SharedPreferences.getInstance();
return await prefs.setInt(key, value);
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
return false;
} catch (e) {
debugPrint('保存整数失败: $e');
return false;
}
}
/// 读取整数
static Future<int?> getInt(String key) async {
try {
final prefs = await SharedPreferences.getInstance();
return prefs.getInt(key);
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
return null;
} catch (e) {
debugPrint('读取整数失败: $e');
return null;
}
}
/// 删除键
static Future<bool> remove(String key) async {
try {
final prefs = await SharedPreferences.getInstance();
return await prefs.remove(key);
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
return false;
} catch (e) {
debugPrint('删除键失败: $e');
return false;
}
}
/// 清空所有数据
static Future<bool> clear() async {
try {
final prefs = await SharedPreferences.getInstance();
return await prefs.clear();
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
return false;
} catch (e) {
debugPrint('清空数据失败: $e');
return false;
}
}
}
使用方法
📋 使用流程图
#mermaid-svg-FRNcjCeuQtMXOmsh{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FRNcjCeuQtMXOmsh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FRNcjCeuQtMXOmsh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FRNcjCeuQtMXOmsh .error-icon{fill:#552222;}#mermaid-svg-FRNcjCeuQtMXOmsh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FRNcjCeuQtMXOmsh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FRNcjCeuQtMXOmsh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FRNcjCeuQtMXOmsh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FRNcjCeuQtMXOmsh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FRNcjCeuQtMXOmsh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FRNcjCeuQtMXOmsh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FRNcjCeuQtMXOmsh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FRNcjCeuQtMXOmsh .marker.cross{stroke:#333333;}#mermaid-svg-FRNcjCeuQtMXOmsh svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FRNcjCeuQtMXOmsh p{margin:0;}#mermaid-svg-FRNcjCeuQtMXOmsh .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-FRNcjCeuQtMXOmsh .cluster-label text{fill:#333;}#mermaid-svg-FRNcjCeuQtMXOmsh .cluster-label span{color:#333;}#mermaid-svg-FRNcjCeuQtMXOmsh .cluster-label span p{background-color:transparent;}#mermaid-svg-FRNcjCeuQtMXOmsh .label text,#mermaid-svg-FRNcjCeuQtMXOmsh span{fill:#333;color:#333;}#mermaid-svg-FRNcjCeuQtMXOmsh .node rect,#mermaid-svg-FRNcjCeuQtMXOmsh .node circle,#mermaid-svg-FRNcjCeuQtMXOmsh .node ellipse,#mermaid-svg-FRNcjCeuQtMXOmsh .node polygon,#mermaid-svg-FRNcjCeuQtMXOmsh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FRNcjCeuQtMXOmsh .rough-node .label text,#mermaid-svg-FRNcjCeuQtMXOmsh .node .label text,#mermaid-svg-FRNcjCeuQtMXOmsh .image-shape .label,#mermaid-svg-FRNcjCeuQtMXOmsh .icon-shape .label{text-anchor:middle;}#mermaid-svg-FRNcjCeuQtMXOmsh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FRNcjCeuQtMXOmsh .rough-node .label,#mermaid-svg-FRNcjCeuQtMXOmsh .node .label,#mermaid-svg-FRNcjCeuQtMXOmsh .image-shape .label,#mermaid-svg-FRNcjCeuQtMXOmsh .icon-shape .label{text-align:center;}#mermaid-svg-FRNcjCeuQtMXOmsh .node.clickable{cursor:pointer;}#mermaid-svg-FRNcjCeuQtMXOmsh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FRNcjCeuQtMXOmsh .arrowheadPath{fill:#333333;}#mermaid-svg-FRNcjCeuQtMXOmsh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FRNcjCeuQtMXOmsh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FRNcjCeuQtMXOmsh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FRNcjCeuQtMXOmsh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FRNcjCeuQtMXOmsh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FRNcjCeuQtMXOmsh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FRNcjCeuQtMXOmsh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FRNcjCeuQtMXOmsh .cluster text{fill:#333;}#mermaid-svg-FRNcjCeuQtMXOmsh .cluster span{color:#333;}#mermaid-svg-FRNcjCeuQtMXOmsh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FRNcjCeuQtMXOmsh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FRNcjCeuQtMXOmsh rect.text{fill:none;stroke-width:0;}#mermaid-svg-FRNcjCeuQtMXOmsh .icon-shape,#mermaid-svg-FRNcjCeuQtMXOmsh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FRNcjCeuQtMXOmsh .icon-shape p,#mermaid-svg-FRNcjCeuQtMXOmsh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FRNcjCeuQtMXOmsh .icon-shape rect,#mermaid-svg-FRNcjCeuQtMXOmsh .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FRNcjCeuQtMXOmsh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FRNcjCeuQtMXOmsh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FRNcjCeuQtMXOmsh :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
直接使用
使用封装
🎯 开始使用 SharedPreferences
选择使用方式
📝 直接调用 SharedPreferences API
📝 调用 PreferencesHelper 方法
获取实例
执行操作
处理结果
调用静态方法
自动处理错误
返回结果
✅ 完成
🎯 方法 1:直接使用 SharedPreferences
📝 示例 1:保存和读取字符串
文件路径: lib/screens/home_page.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _currentCity = '北京';
String _currentLocationId = '101010100';
void initState() {
super.initState();
_loadCurrentCity();
}
/// 📥 从 SharedPreferences 加载当前城市
Future<void> _loadCurrentCity() async {
try {
final prefs = await SharedPreferences.getInstance();
final cityName = prefs.getString('current_city_name');
final locationId = prefs.getString('current_location_id');
if (cityName != null && locationId != null) {
setState(() {
_currentCity = cityName;
_currentLocationId = locationId;
});
}
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
debugPrint('提示:请完全停止应用后重新运行(不要使用热重载)');
} catch (e) {
debugPrint('加载当前城市失败: $e');
}
}
/// 💾 保存当前城市到 SharedPreferences
Future<void> _saveCurrentCity(String cityName, String locationId) async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('current_city_name', cityName);
await prefs.setString('current_location_id', locationId);
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
debugPrint('提示:请完全停止应用后重新运行(不要使用热重载)');
} catch (e) {
debugPrint('保存当前城市失败: $e');
}
}
/// 🔄 切换城市
Future<void> _switchCity(String cityName, String locationId) async {
setState(() {
_currentCity = cityName;
_currentLocationId = locationId;
});
await _saveCurrentCity(cityName, locationId);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_currentCity),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('当前城市: $_currentCity'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () => _switchCity('上海', '101020100'),
child: const Text('切换到上海'),
),
],
),
),
);
}
}
📝 代码解读:
导入库:
- package:shared_preferences/shared_preferences.dart: SharedPreferences 库
- package:flutter/services.dart: MissingPluginException 异常
读取数据:
- SharedPreferences.getInstance(): 获取实例
- getString(key): 读取字符串值
- 使用 null 检查判断键是否存在
保存数据:
- setString(key, value): 保存字符串值
- 使用 await 等待操作完成
错误处理:
- MissingPluginException: 处理插件未注册错误
- catch (e): 处理其他错误
📝 示例 2:保存和读取复杂数据(JSON)
文件路径: lib/screens/city_manage_page.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import '../models/weather_models.dart';
class CityManagePage extends StatefulWidget {
const CityManagePage({super.key});
State<CityManagePage> createState() => _CityManagePageState();
}
class _CityManagePageState extends State<CityManagePage> {
List<Location> _cities = [];
void initState() {
super.initState();
_loadCities();
}
/// 📥 从 SharedPreferences 加载城市列表
Future<void> _loadCities() async {
try {
final prefs = await SharedPreferences.getInstance();
final citiesJson = prefs.getString('saved_cities');
if (citiesJson != null && citiesJson.isNotEmpty) {
final List<dynamic> citiesList = jsonDecode(citiesJson);
final cities = citiesList
.map((json) => Location.fromJson(json as Map<String, dynamic>))
.toList();
setState(() {
_cities = cities;
});
}
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
debugPrint('提示:请完全停止应用后重新运行(不要使用热重载)');
} catch (e) {
debugPrint('加载城市列表失败: $e');
}
}
/// 💾 保存城市列表到 SharedPreferences
Future<void> _saveCities() async {
try {
final prefs = await SharedPreferences.getInstance();
final citiesJson = jsonEncode(
_cities.map((city) => city.toJson()).toList(),
);
await prefs.setString('saved_cities', citiesJson);
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
debugPrint('提示:请完全停止应用后重新运行(不要使用热重载)');
} catch (e) {
debugPrint('保存城市列表失败: $e');
}
}
/// ➕ 添加城市
Future<void> _addCity(Location city) async {
setState(() {
_cities.add(city);
});
await _saveCities();
}
/// ➖ 删除城市
Future<void> _removeCity(String cityId) async {
setState(() {
_cities.removeWhere((c) => c.id == cityId);
});
await _saveCities();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('城市管理'),
),
body: ListView.builder(
itemCount: _cities.length,
itemBuilder: (context, index) {
final city = _cities[index];
return ListTile(
title: Text(city.name),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => _removeCity(city.id),
),
);
},
),
);
}
}
📝 代码解读:
JSON 序列化:
- jsonEncode(): 将对象转换为 JSON 字符串
- jsonDecode(): 将 JSON 字符串转换为对象
复杂数据存储:
- 使用 JSON 格式存储复杂对象
- 需要实现 toJson() 和 fromJson() 方法
🎯 方法 2:使用 PreferencesHelper 封装
📝 示例:使用 PreferencesHelper
文件路径: lib/screens/intro_screen.dart
import 'package:flutter/material.dart';
import '../utils/preferences_helper.dart';
class IntroScreen extends StatefulWidget {
const IntroScreen({super.key});
State<IntroScreen> createState() => _IntroScreenState();
}
class _IntroScreenState extends State<IntroScreen> {
void initState() {
super.initState();
_checkFirstLaunch();
}
/// 🔍 检查是否首次启动
Future<void> _checkFirstLaunch() async {
final isFirst = await PreferencesHelper.isFirstLaunch();
if (!isFirst) {
// 如果不是首次启动,直接跳转到主页面
Navigator.pushReplacementNamed(context, '/home');
}
}
/// ✅ 完成引导页
Future<void> _onDone() async {
// 标记已非首次启动
await PreferencesHelper.setNotFirstLaunch();
// 跳转到主页面
Navigator.pushReplacementNamed(context, '/home');
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('欢迎使用应用'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _onDone,
child: const Text('开始使用'),
),
],
),
),
);
}
}
📝 代码解读:
使用封装方法:
- PreferencesHelper.isFirstLaunch(): 检查是否首次启动
- PreferencesHelper.setNotFirstLaunch(): 标记已非首次启动
优势:
- 代码更简洁
- 错误处理已内置
- 类型安全
📋 SharedPreferences 支持的数据类型
#mermaid-svg-IJZrFX2Yha86OyUQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IJZrFX2Yha86OyUQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IJZrFX2Yha86OyUQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IJZrFX2Yha86OyUQ .error-icon{fill:#552222;}#mermaid-svg-IJZrFX2Yha86OyUQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IJZrFX2Yha86OyUQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IJZrFX2Yha86OyUQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IJZrFX2Yha86OyUQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IJZrFX2Yha86OyUQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IJZrFX2Yha86OyUQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IJZrFX2Yha86OyUQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IJZrFX2Yha86OyUQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IJZrFX2Yha86OyUQ .marker.cross{stroke:#333333;}#mermaid-svg-IJZrFX2Yha86OyUQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IJZrFX2Yha86OyUQ p{margin:0;}#mermaid-svg-IJZrFX2Yha86OyUQ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-IJZrFX2Yha86OyUQ .cluster-label text{fill:#333;}#mermaid-svg-IJZrFX2Yha86OyUQ .cluster-label span{color:#333;}#mermaid-svg-IJZrFX2Yha86OyUQ .cluster-label span p{background-color:transparent;}#mermaid-svg-IJZrFX2Yha86OyUQ .label text,#mermaid-svg-IJZrFX2Yha86OyUQ span{fill:#333;color:#333;}#mermaid-svg-IJZrFX2Yha86OyUQ .node rect,#mermaid-svg-IJZrFX2Yha86OyUQ .node circle,#mermaid-svg-IJZrFX2Yha86OyUQ .node ellipse,#mermaid-svg-IJZrFX2Yha86OyUQ .node polygon,#mermaid-svg-IJZrFX2Yha86OyUQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IJZrFX2Yha86OyUQ .rough-node .label text,#mermaid-svg-IJZrFX2Yha86OyUQ .node .label text,#mermaid-svg-IJZrFX2Yha86OyUQ .image-shape .label,#mermaid-svg-IJZrFX2Yha86OyUQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-IJZrFX2Yha86OyUQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IJZrFX2Yha86OyUQ .rough-node .label,#mermaid-svg-IJZrFX2Yha86OyUQ .node .label,#mermaid-svg-IJZrFX2Yha86OyUQ .image-shape .label,#mermaid-svg-IJZrFX2Yha86OyUQ .icon-shape .label{text-align:center;}#mermaid-svg-IJZrFX2Yha86OyUQ .node.clickable{cursor:pointer;}#mermaid-svg-IJZrFX2Yha86OyUQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IJZrFX2Yha86OyUQ .arrowheadPath{fill:#333333;}#mermaid-svg-IJZrFX2Yha86OyUQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IJZrFX2Yha86OyUQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IJZrFX2Yha86OyUQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IJZrFX2Yha86OyUQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IJZrFX2Yha86OyUQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IJZrFX2Yha86OyUQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IJZrFX2Yha86OyUQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IJZrFX2Yha86OyUQ .cluster text{fill:#333;}#mermaid-svg-IJZrFX2Yha86OyUQ .cluster span{color:#333;}#mermaid-svg-IJZrFX2Yha86OyUQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IJZrFX2Yha86OyUQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IJZrFX2Yha86OyUQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-IJZrFX2Yha86OyUQ .icon-shape,#mermaid-svg-IJZrFX2Yha86OyUQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IJZrFX2Yha86OyUQ .icon-shape p,#mermaid-svg-IJZrFX2Yha86OyUQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IJZrFX2Yha86OyUQ .icon-shape rect,#mermaid-svg-IJZrFX2Yha86OyUQ .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IJZrFX2Yha86OyUQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IJZrFX2Yha86OyUQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IJZrFX2Yha86OyUQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
SharedPreferences 数据类型
String 字符串
int 整数
bool 布尔值
double 浮点数
List 字符串列表
setString/getString
setInt/getInt
setBool/getBool
setDouble/getDouble
setStringList/getStringList
数据类型对照表:
| String | setString() | getString() | String? |
| int | setInt() | getInt() | int? |
| bool | setBool() | getBool() | bool? |
| double | setDouble() | getDouble() | double? |
| List | setStringList() | getStringList() | List<String>? |
⚠️ 注意事项:
- 所有读取方法都可能返回 null(键不存在时)
- 复杂对象需要使用 JSON 序列化后存储为 String
- List 只支持 List<String>,不支持其他类型
常见错误及解决方案
📋 错误处理流程图
#mermaid-svg-2vi95Qa3BU6aDVir{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2vi95Qa3BU6aDVir .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2vi95Qa3BU6aDVir .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2vi95Qa3BU6aDVir .error-icon{fill:#552222;}#mermaid-svg-2vi95Qa3BU6aDVir .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2vi95Qa3BU6aDVir .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2vi95Qa3BU6aDVir .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2vi95Qa3BU6aDVir .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2vi95Qa3BU6aDVir .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2vi95Qa3BU6aDVir .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2vi95Qa3BU6aDVir .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2vi95Qa3BU6aDVir .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2vi95Qa3BU6aDVir .marker.cross{stroke:#333333;}#mermaid-svg-2vi95Qa3BU6aDVir svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2vi95Qa3BU6aDVir p{margin:0;}#mermaid-svg-2vi95Qa3BU6aDVir .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-2vi95Qa3BU6aDVir .cluster-label text{fill:#333;}#mermaid-svg-2vi95Qa3BU6aDVir .cluster-label span{color:#333;}#mermaid-svg-2vi95Qa3BU6aDVir .cluster-label span p{background-color:transparent;}#mermaid-svg-2vi95Qa3BU6aDVir .label text,#mermaid-svg-2vi95Qa3BU6aDVir span{fill:#333;color:#333;}#mermaid-svg-2vi95Qa3BU6aDVir .node rect,#mermaid-svg-2vi95Qa3BU6aDVir .node circle,#mermaid-svg-2vi95Qa3BU6aDVir .node ellipse,#mermaid-svg-2vi95Qa3BU6aDVir .node polygon,#mermaid-svg-2vi95Qa3BU6aDVir .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2vi95Qa3BU6aDVir .rough-node .label text,#mermaid-svg-2vi95Qa3BU6aDVir .node .label text,#mermaid-svg-2vi95Qa3BU6aDVir .image-shape .label,#mermaid-svg-2vi95Qa3BU6aDVir .icon-shape .label{text-anchor:middle;}#mermaid-svg-2vi95Qa3BU6aDVir .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2vi95Qa3BU6aDVir .rough-node .label,#mermaid-svg-2vi95Qa3BU6aDVir .node .label,#mermaid-svg-2vi95Qa3BU6aDVir .image-shape .label,#mermaid-svg-2vi95Qa3BU6aDVir .icon-shape .label{text-align:center;}#mermaid-svg-2vi95Qa3BU6aDVir .node.clickable{cursor:pointer;}#mermaid-svg-2vi95Qa3BU6aDVir .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2vi95Qa3BU6aDVir .arrowheadPath{fill:#333333;}#mermaid-svg-2vi95Qa3BU6aDVir .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2vi95Qa3BU6aDVir .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2vi95Qa3BU6aDVir .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2vi95Qa3BU6aDVir .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2vi95Qa3BU6aDVir .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2vi95Qa3BU6aDVir .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2vi95Qa3BU6aDVir .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2vi95Qa3BU6aDVir .cluster text{fill:#333;}#mermaid-svg-2vi95Qa3BU6aDVir .cluster span{color:#333;}#mermaid-svg-2vi95Qa3BU6aDVir div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2vi95Qa3BU6aDVir .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2vi95Qa3BU6aDVir rect.text{fill:none;stroke-width:0;}#mermaid-svg-2vi95Qa3BU6aDVir .icon-shape,#mermaid-svg-2vi95Qa3BU6aDVir .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2vi95Qa3BU6aDVir .icon-shape p,#mermaid-svg-2vi95Qa3BU6aDVir .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2vi95Qa3BU6aDVir .icon-shape rect,#mermaid-svg-2vi95Qa3BU6aDVir .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2vi95Qa3BU6aDVir .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2vi95Qa3BU6aDVir .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2vi95Qa3BU6aDVir :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
MissingPluginException
网络错误
类型错误
权限错误
❌ 发生错误
错误类型
插件未注册错误
Git 依赖下载失败
数据类型不匹配
存储权限不足
完全重启应用不要使用热重载
检查网络连接配置 Git 代理
检查数据类型使用正确的读写方法
检查应用权限确保有存储权限
✅ 问题解决
❌ 错误 1:MissingPluginException
错误信息:
MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)
原因分析:
- 🔥 最常见的原因:热重载后插件未重新注册
- 🔧 插件未正确初始化
- 📦 依赖未正确安装
解决方案:
完全重启应用(推荐):
# 停止应用
# 重新运行
flutter run
检查依赖安装:
flutter pub get
flutter clean
flutter pub get
添加错误处理:
try {
final prefs = await SharedPreferences.getInstance();
// 使用 prefs
} on MissingPluginException catch (e) {
debugPrint('SharedPreferences 插件未注册: $e');
debugPrint('提示:请完全停止应用后重新运行(不要使用热重载)');
// 返回默认值或显示错误提示
}
预防措施:
- ✅ 始终使用完全重启,避免热重载
- ✅ 添加 MissingPluginException 异常处理
- ✅ 提供默认值,确保应用不会崩溃
❌ 错误 2:Git 依赖下载失败
错误信息:
Git error. Command: git fetch
fatal: unable to access 'https://gitcode.com/…': Failed to connect
原因分析:
- 🌐 网络连接问题
- 🔒 Git 代理配置问题
- 🔑 仓库访问权限问题
解决方案:
检查网络连接:
ping gitcode.com
配置 Git 代理(如果需要):
git config –global http.proxy http://proxy.example.com:8080
git config –global https.proxy https://proxy.example.com:8080
手动下载依赖:
# 克隆仓库到本地
git clone https://gitcode.com/openharmony-tpc/flutter_packages.git
# 使用本地路径
预防措施:
- ✅ 确保网络连接正常
- ✅ 配置 Git 代理(如果需要)
- ✅ 使用稳定的网络环境
❌ 错误 3:数据类型不匹配
错误信息:
TypeError: type 'String' is not a subtype of type 'int' in type cast
原因分析:
- 🔢 使用错误的数据类型方法
- 📝 键名拼写错误
- 🔄 数据类型转换错误
解决方案:
检查数据类型:
// ❌ 错误示例
final value = prefs.getInt('key'); // 但实际存储的是 String
// ✅ 正确示例
final value = prefs.getString('key');
使用类型安全的方法:
// ✅ 使用封装方法,类型安全
final value = await PreferencesHelper.getString('key');
添加类型检查:
final value = prefs.get('key');
if (value is String) {
// 处理 String 类型
} else if (value is int) {
// 处理 int 类型
}
预防措施:
- ✅ 使用常量管理键名
- ✅ 使用封装方法,类型安全
- ✅ 添加类型检查
❌ 错误 4:存储权限不足
错误信息:
Permission denied: /data/data/com.example.app/shared_prefs
原因分析:
- 🔒 应用没有存储权限
- 📱 鸿蒙系统权限配置问题
- 🛡️ 安全策略限制
解决方案:
检查权限配置:
<!– ohos/module.json5 –>
{
"requestPermissions": [
{
"name": "ohos.permission.WRITE_USER_STORAGE",
"reason": "需要存储权限以保存用户数据"
},
{
"name": "ohos.permission.READ_USER_STORAGE",
"reason": "需要读取权限以加载用户数据"
}
]
}
运行时请求权限:
import 'package:permission_handler/permission_handler.dart';
Future<bool> requestStoragePermission() async {
final status = await Permission.storage.request();
return status.isGranted;
}
添加错误处理:
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('key', 'value');
} catch (e) {
if (e.toString().contains('Permission denied')) {
// 请求权限
await requestStoragePermission();
}
}
预防措施:
- ✅ 在配置文件中声明权限
- ✅ 运行时请求权限
- ✅ 添加权限检查
❌ 错误 5:数据丢失
错误信息:
数据读取为 null,但之前已保存
原因分析:
- 🗑️ 应用被卸载重装
- 🔄 应用数据被清除
- 💾 存储空间不足
解决方案:
添加数据验证:
Future<String?> getStringSafe(String key) async {
try {
final prefs = await SharedPreferences.getInstance();
final value = prefs.getString(key);
if (value == null) {
// 返回默认值
return 'default_value';
}
return value;
} catch (e) {
// 返回默认值
return 'default_value';
}
}
使用备份机制:
// 保存到多个位置
await prefs.setString('key', 'value');
await prefs.setString('key_backup', 'value');
添加数据迁移:
Future<void> migrateData() async {
final prefs = await SharedPreferences.getInstance();
final oldValue = prefs.getString('old_key');
if (oldValue != null) {
await prefs.setString('new_key', oldValue);
await prefs.remove('old_key');
}
}
预防措施:
- ✅ 提供默认值
- ✅ 添加数据验证
- ✅ 实现数据迁移机制
📋 错误排查检查清单
#mermaid-svg-JbrDJHZ7j0U2R4T1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .error-icon{fill:#552222;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .marker.cross{stroke:#333333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JbrDJHZ7j0U2R4T1 p{margin:0;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .cluster-label text{fill:#333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .cluster-label span{color:#333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .cluster-label span p{background-color:transparent;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .label text,#mermaid-svg-JbrDJHZ7j0U2R4T1 span{fill:#333;color:#333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .node rect,#mermaid-svg-JbrDJHZ7j0U2R4T1 .node circle,#mermaid-svg-JbrDJHZ7j0U2R4T1 .node ellipse,#mermaid-svg-JbrDJHZ7j0U2R4T1 .node polygon,#mermaid-svg-JbrDJHZ7j0U2R4T1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .rough-node .label text,#mermaid-svg-JbrDJHZ7j0U2R4T1 .node .label text,#mermaid-svg-JbrDJHZ7j0U2R4T1 .image-shape .label,#mermaid-svg-JbrDJHZ7j0U2R4T1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .rough-node .label,#mermaid-svg-JbrDJHZ7j0U2R4T1 .node .label,#mermaid-svg-JbrDJHZ7j0U2R4T1 .image-shape .label,#mermaid-svg-JbrDJHZ7j0U2R4T1 .icon-shape .label{text-align:center;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .node.clickable{cursor:pointer;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .arrowheadPath{fill:#333333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JbrDJHZ7j0U2R4T1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JbrDJHZ7j0U2R4T1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JbrDJHZ7j0U2R4T1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .cluster text{fill:#333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .cluster span{color:#333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JbrDJHZ7j0U2R4T1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .icon-shape,#mermaid-svg-JbrDJHZ7j0U2R4T1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .icon-shape p,#mermaid-svg-JbrDJHZ7j0U2R4T1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .icon-shape rect,#mermaid-svg-JbrDJHZ7j0U2R4T1 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JbrDJHZ7j0U2R4T1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JbrDJHZ7j0U2R4T1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JbrDJHZ7j0U2R4T1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
MissingPluginException
网络错误
类型错误
权限错误
是
否
遇到错误
检查错误信息
错误类型
完全重启应用
检查网络连接
检查数据类型
检查权限配置
问题解决?
✅ 完成
查看日志
搜索解决方案
联系技术支持
检查清单:
- ✅ 是否完全重启了应用(不是热重载)?
- ✅ 是否添加了 MissingPluginException 异常处理?
- ✅ 网络连接是否正常?
- ✅ Git 依赖是否正确安装?
- ✅ 数据类型是否匹配?
- ✅ 权限是否已配置?
- ✅ 键名是否正确?
总结
📋 本教程完成的内容
本教程详细介绍了如何在 Flutter 鸿蒙项目中使用鸿蒙化适配的 shared_preferences 库进行本地数据持久化存储。主要内容包括:
💡 关键要点
- ⚙️ Git 依赖引入:必须使用 Git 方式引入鸿蒙适配版本,不能使用 pub.dev 版本
- 📝 二次封装:推荐使用封装类统一管理键名和错误处理
- 🔒 错误处理:必须处理 MissingPluginException 异常,避免热重载问题
- 💾 数据类型:只支持基本数据类型,复杂对象需要使用 JSON 序列化
- 🛡️ 权限配置:确保在配置文件中声明存储权限
📚 相关资源
- SharedPreferences 官方文档: https://pub.dev/packages/shared_preferences
- OpenHarmony TPC 仓库: https://atomgit.com/openharmony-tpc/flutter_packages
- Flutter 官方文档: https://flutter.dev/docs
🎉 祝你开发顺利! 🚀 欢迎加入开源鸿蒙跨平台社区






