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

相关推荐
Fate_I_C2 小时前
Flutter鸿蒙0-1开发-工具环境篇
flutter·华为·harmonyos·鸿蒙
走在路上的菜鸟2 小时前
Android学Dart学习笔记第二十四节 类-可调用对象Class()()
android·笔记·学习·flutter
2501_915921432 小时前
Flutter App 到底该怎么测试?如何在 iOS 上进行测试
android·flutter·ios·小程序·uni-app·cocoa·iphone
Fate_I_C3 小时前
Flutter鸿蒙0-1开发-flutter create <prjn>
flutter·华为·harmonyos·鸿蒙
走在路上的菜鸟3 小时前
Android学Dart学习笔记第二十五节 类修饰符
android·笔记·学习·flutter
kirk_wang3 小时前
Flutter animations 库在 OpenHarmony 平台的适配与性能优化实践
flutter·移动开发·跨平台·arkts·鸿蒙
Bigger12 小时前
Flutter 开发实战:解决华为 HarmonyOS 任务列表不显示 App 名称的终极指南
android·flutter·华为
梧桐ty16 小时前
鸿蒙应用冷启动优化:Flutter首屏秒开与白屏治理实战
flutter·华为·harmonyos
梧桐ty16 小时前
驾驭未来:基于鸿蒙的Flutter车载应用与手机端协同实战
flutter·华为·harmonyos