欢迎光临
我们一直在努力

Flutter本地数据持久化存储三方库shared_preferences已完成鸿蒙化适配,快来体验一把

目录

  • 概述
  • 引入三方库步骤
  • 二次封装实现
  • 使用方法
  • 常见错误及解决方案
  • 总结

  • 概述

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

    🎯 本教程目标

    通过本教程,你将学会:

  • ✅ 如何在 Flutter 项目中引入鸿蒙化适配的 shared_preferences 库(使用 Git 方式)
  • ✅ 如何对 shared_preferences 进行二次封装,提高代码复用性和可维护性
  • ✅ 如何在项目中使用 shared_preferences 实现数据持久化存储
  • ✅ 如何处理常见错误和异常情况
  • ✅ 最终在鸿蒙设备上实现稳定的本地数据存储功能
  • image-20260127155026278

    📁 项目文件结构

    在开始之前,让我们先了解一下项目结构:

    lib/
    ├── utils/ # 工具类目录
    │ └── preferences_helper.dart # 🔧 SharedPreferences 二次封装工具类
    └── screens/ # 页面文件目录
    ├── home_page.dart # 🏠 首页(使用 SharedPreferences 保存当前城市)
    └── city_manage_page.dart # 🏙️ 城市管理页(使用 SharedPreferences 保存城市列表)

    🎯 本教程将创建的文件(按顺序)

    严格按照以下顺序创建文件,每个文件创建后立即验证:

  • pubspec.yaml – 📝 配置鸿蒙化适配的 shared_preferences 依赖
  • lib/utils/preferences_helper.dart – 🔧 SharedPreferences 二次封装工具类
  • lib/screens/home_page.dart – 🏠 首页示例(使用 SharedPreferences)
  • lib/screens/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(项目根目录)

    操作说明:

  • 📂 在 IDE 中打开项目根目录
  • 📄 找到并打开 pubspec.yaml 文件
  • 👀 确认文件内容,找到 dependencies: 部分
  • 📝 步骤 2:添加 Git 依赖配置

    位置: pubspec.yaml 文件的 dependencies: 部分

    操作步骤:

  • 📍 找到 dependencies: 部分
  • 📝 在 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:保存文件

    操作说明:

  • 💾 保存 pubspec.yaml 文件
  • ✅ 确认文件已保存成功
  • 📝 步骤 4:运行 flutter pub get

    操作步骤:

  • 💻 打开终端(Terminal)
  • 📂 切换到项目根目录
  • ⬇️ 运行以下命令:
  • flutter pub get

    预期输出:

    Resolving dependencies…
    Downloading packages…
    + shared_preferences (from git)
    Got dependencies!

    ✅ 成功标志:

    • 看到 Got dependencies! 提示
    • 没有错误信息
    • .dart_tool/package_config.json 文件中包含 shared_preferences

    📝 步骤 5:验证安装

    操作步骤:

  • 🔍 检查 .dart_tool/package_config.json 文件
  • 🔍 搜索 shared_preferences,确认已正确安装
  • 📝 在代码中尝试导入:
  • 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

    操作步骤:

  • 📂 在 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 方式引入鸿蒙适配版本的 shared_preferences
  • ✅ 🔧 二次封装:创建 PreferencesHelper 工具类,提高代码复用性和可维护性
  • ✅ 💡 使用方法:提供了直接使用和封装使用两种方式的完整示例
  • ✅ ⚠️ 错误处理:详细介绍了常见错误及解决方案,帮助新手快速解决问题
  • 💡 关键要点

    • ⚙️ 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

    🎉 祝你开发顺利! 🚀 欢迎加入开源鸿蒙跨平台社区

    赞(0)
    未经允许不得转载:171主机测评 » Flutter本地数据持久化存储三方库shared_preferences已完成鸿蒙化适配,快来体验一把
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址