目录
[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 小结)
一、简介
SharedPreferences 是 Flutter官方提供的轻量级本地数据存储方案,适用于保存用户配置、登录状态、简单设置等键值对(Key-Value)数据。
其底层基于平台原生实现(Android 的 SharedPreferences 和 iOS 的 NSUserDefaults),具有操作简单、读写高效的特点。
二、核心概念
|--------|-----------------------------------------------|
| 特性 | 说明 |
| 数据类型支持 | int、double、bool、String、List<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 实例,这种做法有两个明显的弊端:
-
代码重复:每个页面都要写一遍异步等待。
-
硬编码:
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 在项目中使用
-
入口预装载 在 main.dart 的 main 函数中,先确保 Flutter 引擎初始化,然后加载 SP。
void main() async { // 必须调用,确保与原生层交互正常 WidgetsFlutterBinding.ensureInitialized(); // 预装载 SP,耗时极短,但收益巨大 await SPUtils.init(); runApp(const MyApp()); } -
业务逻辑层调用 在任何 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 适合存储小体量的配置信息(如登录态、主题颜色、引导页状态),如果是大批量的结构化数据,建议使用
Hive或SQFlite。
五、总结
SharedPreferences 是 Flutter 生态中最基础的持久化方案,具有上手简单、跨平台兼容的优势,适用于存储小型、非敏感的键值对数据。