3、SharedPreferences基础
好,咱们今天来聊聊 SharedPreferences。说实话,这是 Flutter 本地存储里最基础、最常用的一个东西。你想想看,很多时候我们只需要存点简单的配置信息——比如用户登录状态、主题颜色、音量大小——根本用不上数据库那种重型武器。这时候,SharedPreferences 就是你的最佳选择。
什么是 SharedPreferences?
简单来说,SharedPreferences 就是一个轻量级的键值对存储方案。它把数据以 XML 文件的形式存在设备本地。每个键对应一个值,就像你手机里的通讯录——名字是键,电话号码是值。
我个人习惯把它叫做「小纸条存储」。为什么?因为它只能存一些简单的数据类型:字符串、整数、布尔值、浮点数、还有字符串列表。你想存个复杂对象?不好意思,得先序列化成 JSON 字符串才行。
核心特点:
- 基于键值对(Key-Value)
- 数据存储在应用沙盒内
- 应用卸载时数据自动清除
- 读写速度极快(毫秒级)
适用场景与限制
我在项目中遇到过不少同学,一上来就想用 SharedPreferences 存所有东西。嗯,这里要注意——它可不是万能的。
| 适用场景 | 不适用场景 |
|---|---|
| 用户偏好设置(主题、语言) | 大量结构化数据(如用户列表) |
| 登录状态标记 | 需要复杂查询的数据 |
| 缓存简单的配置信息 | 敏感信息(密码、Token) |
| 应用首次启动标记 | 超过 100KB 的数据 |
说白了,SharedPreferences 适合存「配置」,不适合存「数据」。你想想看,谁会拿小纸条去记一本小说呢?
避坑指南:我曾经在一个项目里用 SharedPreferences 存了上千条商品缓存数据,结果应用启动时卡了足足 3 秒。后来才意识到,这玩意儿加载时会把整个文件读进内存。数据量一大,性能就崩了。
基本读写操作
好,咱们直接上代码。Flutter 里使用 SharedPreferences 需要先引入包:
dependencies:
shared_preferences: ^2.2.0
然后导入:
import 'package:shared_preferences/shared_preferences.dart';
写操作(set 方法)
写数据很简单,调用对应的 set 方法就行:
// 获取实例
final prefs = await SharedPreferences.getInstance();
// 存储不同类型的数据
await prefs.setString('username', '张三');
await prefs.setInt('age', 25);
await prefs.setBool('isLoggedIn', true);
await prefs.setDouble('volume', 0.8);
await prefs.setStringList('tags', ['flutter', 'dart']);
注意看,每个 set 方法前面都有 await。为什么?因为 SharedPreferences 的写操作是异步的。我刚开始学的时候老忘记加 await,结果数据死活存不进去——嗯,这个坑我替你们踩过了。
读操作(get 方法)
读数据同样简单:
final prefs = await SharedPreferences.getInstance();
String? username = prefs.getString('username');
int? age = prefs.getInt('age');
bool? isLoggedIn = prefs.getBool('isLoggedIn');
double? volume = prefs.getDouble('volume');
List<String>? tags = prefs.getStringList('tags');
看到没?返回值都是可空类型(带问号)。这是因为如果键不存在,get 方法会返回 null。我个人习惯在读取后加个默认值:
String username = prefs.getString('username') ?? '默认用户';
小技巧:你可以用 prefs.containsKey('key') 先检查键是否存在,避免频繁使用空合并运算符。
同步与异步方法
这里有个重要的点要讲清楚。SharedPreferences 提供了两种调用方式:
| 方法类型 | 示例 | 说明 |
|---|---|---|
| 异步方法 | await prefs.setString() |
推荐使用,不会阻塞 UI |
| 同步方法 | prefs.getString()(读取时) |
读取操作实际上是同步的 |
你可能会问:「为什么读取是同步的,写入却是异步的?」
原因很简单。SharedPreferences 在初始化时会把整个文件加载到内存缓存里。所以读取操作直接从内存拿数据,快得很,不需要异步。但写入操作需要把数据写回磁盘文件,这个 I/O 操作是耗时的,所以设计成了异步。
我记得有一次在面试里被问到这个问题,当时没答上来。后来翻源码才搞明白——其实 getString() 内部就是直接返回缓存里的值,根本没有异步操作。
注意:虽然读取是同步的,但获取实例 SharedPreferences.getInstance() 是异步的。你必须在获取实例后才能读写数据。
完整示例
最后,给你一个完整的实战代码。假设我们要做一个简单的主题切换功能:
class ThemePreferences {
static const String _keyTheme = 'theme_mode';
// 保存主题模式
static Future<void> saveThemeMode(String mode) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_keyTheme, mode);
}
// 读取主题模式
static Future<String> getThemeMode() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(_keyTheme) ?? 'light';
}
// 删除主题设置
static Future<void> clearThemeMode() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_keyTheme);
}
}
使用起来也很直观:
// 保存
await ThemePreferences.saveThemeMode('dark');
// 读取
String mode = await ThemePreferences.getThemeMode();
// 删除
await ThemePreferences.clearThemeMode();
好了,SharedPreferences 的基础内容就这些。说白了,它就是个小而美的工具——适合存配置,别拿来存大数据。下一节我们会聊到如何用它实现更复杂的场景,比如用户登录状态的持久化。到时候再细聊。