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

相关推荐
灰灰勇闯IT4 小时前
Flutter for OpenHarmony:自定义 Paint 绘图 —— 释放 Canvas 的创造力
flutter
2601_949833394 小时前
flutter_for_openharmony口腔护理app实战+预约管理实现
android·javascript·flutter
牛马1116 小时前
Flutter OverlayEntry
flutter
2603_949462107 小时前
Flutter for OpenHarmony社团管理App实战:预算管理实现
android·javascript·flutter
2601_949975798 小时前
Flutter for OpenHarmony艺考真题题库+帮助中心实现
flutter
子春一11 小时前
Flutter for OpenHarmony:构建一个 Flutter 井字棋游戏,深入解析状态驱动逻辑、胜利判定与极简交互设计
flutter·游戏·交互
雨季66611 小时前
Flutter 三端应用实战:OpenHarmony “极简手势轨迹球”——指尖与屏幕的诗意对话
开发语言·javascript·flutter
ujainu12 小时前
Flutter + OpenHarmony 游戏开发进阶:CustomPainter 手绘游戏世界——从球体到轨道
flutter·游戏·信息可视化·openharmony
雨季66612 小时前
Flutter 三端应用实战:OpenHarmony “专注时光盒”——在碎片洪流中守护心流的数字容器
开发语言·前端·安全·flutter·交互
kirk_wang12 小时前
Flutter艺术探索-Flutter相机与相册:camera库与image_picker集成
flutter·移动开发·flutter教程·移动开发教程