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

相关推荐
奋斗的小青年!!11 小时前
Flutter浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙
程序员老刘14 小时前
一杯奶茶钱,PicGo + 阿里云 OSS 搭建永久稳定的个人图床
flutter·markdown
奋斗的小青年!!18 小时前
OpenHarmony Flutter 拖拽排序组件性能优化与跨平台适配指南
flutter·harmonyos·鸿蒙
小雨下雨的雨19 小时前
Flutter 框架跨平台鸿蒙开发 —— Stack 控件之三维层叠艺术
flutter·华为·harmonyos
行者9620 小时前
OpenHarmony平台Flutter手风琴菜单组件的跨平台适配实践
flutter·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Flex 控件之响应式弹性布局
flutter·ui·华为·harmonyos·鸿蒙系统
cn_mengbei1 天前
Flutter for OpenHarmony 实战:CheckboxListTile 复选框列表项详解
flutter
cn_mengbei1 天前
Flutter for OpenHarmony 实战:Switch 开关按钮详解
flutter
奋斗的小青年!!1 天前
OpenHarmony Flutter实战:打造高性能订单确认流程步骤条
flutter·harmonyos·鸿蒙
Coder_Boy_1 天前
Flutter基础介绍-跨平台移动应用开发框架
spring boot·flutter