Flutter 数据存储之 SharedPreferences 键值对存储

目录

一、简介

二、核心概念

三、使用步骤

[1. 添加依赖](#1. 添加依赖)

[2. 导入包](#2. 导入包)

[3. 获取实例](#3. 获取实例)

[4. 数据操作方法](#4. 数据操作方法)

四、工程化封装

[4.1 为什么要封装?](#4.1 为什么要封装?)

[4.2 统一键值管理:SPKeys](#4.2 统一键值管理:SPKeys)

[4.3 工具类实现:SPUtils](#4.3 工具类实现:SPUtils)

[4.4 在项目中使用](#4.4 在项目中使用)

[4.5 小结](#4.5 小结)

五、总结


一、简介

SharedPreferencesFlutter官方提供的轻量级本地数据存储方案,适用于保存用户配置、登录状态、简单设置等键值对(Key-Value)数据。

其底层基于平台原生实现(Android 的 SharedPreferences 和 iOS 的 NSUserDefaults),具有操作简单、读写高效的特点。

二、核心概念

|--------|-----------------------------------------------|
| 特性 | 说明 |
| 数据类型支持 | intdoubleboolStringList<String> |
| 存储位置 | 自动持久化到设备本地 |
| 数据持久性 | 应用卸载时数据会被清除 |
| 异步操作 | 所有读写操作均为异步(Future) |
| 跨平台一致性 | 自动适配 Android/iOS/Web/桌面端 |

三、使用步骤

1. 添加依赖

复制代码
# pubspec.yaml
dependencies:
  # 存储键值对数据的轻量级解决方案,适用于保存用户设置、偏好等小型数据。
  shared_preferences: ^2.5.4

2. 导入包

复制代码
import 'package:shared_preferences/shared_preferences.dart';

3. 获取实例

复制代码
final prefs = await SharedPreferences.getInstance();

4. 数据操作方法

|------|-------------------------------------------------|-------------------------------------------|
| 操作类型 | 方法签名 | 示例 |
| 写入数据 | setInt(String key, int value) | prefs.setInt('age', 25); |
| | setDouble(String key, double value) | prefs.setDouble('price', 9.99); |
| | setBool(String key, bool value) | prefs.setBool('isDark', true); |
| | setString(String key, String value) | prefs.setString('name', 'Tom'); |
| | setStringList(String key, List<String> value) | prefs.setStringList('tags', ['a','b']); |
| 读取数据 | get(String key) / getInt() / getBool() 等 | prefs.getInt('age') ?? 0; |
| 删除数据 | remove(String key) | prefs.remove('tempData'); |
| 清空数据 | clear() | prefs.clear(); |

四、工程化封装

在 Flutter 开发中,shared_preferences (SP) 几乎是每个项目的标配,但在实际搬砖过程中,如果直接在业务层满大街写 SharedPreferences.getInstance(),不仅代码显得臃肿,后期维护 Key 值更是一场灾难。今天分享一套我在项目中沉淀的 SP 封装方案,通过 工具类化 和 常量化,实现逻辑解耦。

4.1 为什么要封装?

很多新手喜欢在 initState 里异步获取 SP 实例,这种做法有两个明显的弊端:

  1. 代码重复:每个页面都要写一遍异步等待。

  2. 硬编码:prefs.getString('user_token') 里的字符串一旦写错一个字母,排查起来非常头疼。

我们的目标是:全局初始化一次,到处同步调用,Key 值统一管理

4.2 统一键值管理:SPKeys

首先,新建 sp_keys.dart,我们利用私有构造函数,确保 Key 值只在一个地方定义。

复制代码
/// Description: 定义存储键值对的键
/// CreateDate:  2025/12/22 11:31
/// Author:      agg
class SPKeys {
  SPKeys._(); // 闭合构造函数,防止被实例化

  static const String userToken = 'user_token';
  static const String themeMode = 'theme_mode';
  static const String userInfo = 'user_info';
}

4.3 工具类实现:SPUtils

sp_utils.dart 中,我们引入 late 关键字,通过在应用启动时预加载,将后续的 IO 操作转化为类似同步的调用。

复制代码
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';

///
/// Description:    存储键值对数据工具类
/// CreateDate:     2025/12/22 9:54
/// Author:         agg
///
class SPUtils {
  // 使用 late 关键字,如果未初始化就调用会直接报错,方便开发者定位
  static late SharedPreferences _prefs;

  /// 初始化:在 main.dart 中 await SPUtils.init()
  static Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
  }

  // ==================== 核心方法 ====================
  /// 保存数据
  /// 支持 String, int, double, bool, List<String>
  /// 以及可以通过 jsonEncode 转换的 Map 或 Object
  static Future<bool> set(String key, dynamic value) {
    if (value is String) return _prefs.setString(key, value);
    if (value is int) return _prefs.setInt(key, value);
    if (value is bool) return _prefs.setBool(key, value);
    if (value is double) return _prefs.setDouble(key, value);
    if (value is List<String>) return _prefs.setStringList(key, value);

    // 如果是 Map 或自定义对象,转为 json 字符串存储
    return _prefs.setString(key, jsonEncode(value));
  }

  static String getString(String key, {String defaultValue = ''}) {
    return _prefs.getString(key) ?? defaultValue;
  }

  static int getInt(String key, {int defaultValue = 0}) {
    return _prefs.getInt(key) ?? defaultValue;
  }

  static bool getBool(String key, {bool defaultValue = false}) {
    return _prefs.getBool(key) ?? defaultValue;
  }

  static double getDouble(String key, {double defaultValue = 0.0}) {
    return _prefs.getDouble(key) ?? defaultValue;
  }

  static List<String> getStringList(
    String key, {
    List<String> defaultValue = const [],
  }) {
    return _prefs.getStringList(key) ?? defaultValue;
  }

  /// 获取复杂对象 (Map 或 List)
  /// 读取后需手动转换类型: Map user = SPUtils.getObject('user');
  static dynamic getObject(String key) {
    String? jsonStr = _prefs.getString(key);
    if (jsonStr == null || jsonStr.isEmpty) return null;
    try {
      return jsonDecode(jsonStr);
    } catch (e) {
      print("SPUtils getObject error: $e");
      return null;
    }
  }

  // ==================== 工具方法 ====================
  static bool containsKey(String key) => _prefs.containsKey(key);

  static Future<bool> remove(String key) => _prefs.remove(key);

  static Future<bool> clear() => _prefs.clear();
}

4.4 在项目中使用

  1. 入口预装载 在 main.dart 的 main 函数中,先确保 Flutter 引擎初始化,然后加载 SP。

    复制代码
    void main() async {
      // 必须调用,确保与原生层交互正常
      WidgetsFlutterBinding.ensureInitialized();
      
      // 预装载 SP,耗时极短,但收益巨大
      await SPUtils.init();
      
      runApp(const MyApp());
    }
  2. 业务逻辑层调用 在任何 Widget 或 Controller 里读写数据,都不再需要 await 获取实例。

    // 存储 Token
    SPUtils.set(SPKeys.userToken, "ey...123");

    // 存储复杂的用户信息
    Map<String, dynamic> user = {"name": "agg", "age": 18};
    SPUtils.set(SPKeys.userInfo, user);

    // 获取数据
    String token = SPUtils.getString(SPKeys.userToken);

4.5 小结

  • 同步化体验:虽然 SP 的原生 API 是异步的,但通过在 main 函数预加载实例,我们可以像操作内存变量一样操作本地持久化数据。
  • 安全提示:由于使用了 late,如果在 init() 还没完成时就强行调用读取方法,程序会 Crash,所以务必在 runApp 之前完成初始化。
  • 选型建议:SP 适合存储小体量的配置信息(如登录态、主题颜色、引导页状态),如果是大批量的结构化数据,建议使用 HiveSQFlite

五、总结

SharedPreferences 是 Flutter 生态中最基础的持久化方案,具有上手简单、跨平台兼容的优势,适用于存储小型、非敏感的键值对数据。

官方文档:SharedPreferences API

相关推荐
ujainu38 分钟前
Flutter + OpenHarmony 实现经典打砖块游戏开发实战—— 物理反弹、碰撞检测与关卡系统
flutter·游戏·openharmony·arkanoid·breakout
微祎_1 小时前
构建一个 Flutter 点击速度测试器:深入解析实时交互、性能度量与响应式 UI 设计
flutter·ui·交互
王码码20351 小时前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
ZH15455891312 小时前
Flutter for OpenHarmony Python学习助手实战:Web开发框架应用的实现
python·学习·flutter
晚霞的不甘2 小时前
Flutter for OpenHarmony 构建简洁高效的待办事项应用 实战解析
flutter·ui·前端框架·交互·鸿蒙
百锦再2 小时前
Vue高阶知识:利用 defineModel 特性开发搜索组件组合
前端·vue.js·学习·flutter·typescript·前端框架
廖松洋(Alina)2 小时前
【收尾以及复盘】flutter开发鸿蒙APP之成就徽章页面
flutter·华为·开源·harmonyos·鸿蒙
ZH15455891313 小时前
Flutter for OpenHarmony Python学习助手实战:机器学习算法实现的实现
python·学习·flutter
廖松洋(Alina)3 小时前
【收尾以及复盘】flutter开发鸿蒙APP之打卡日历页面
flutter·华为·开源·harmonyos·鸿蒙
廖松洋(Alina)3 小时前
【收尾以及复盘】flutter开发鸿蒙APP之本月数据统计页面
flutter·华为·开源·harmonyos·鸿蒙