一、简介
shared_preferences 是 Flutter 官方提供的键值对(Key-Value) 本地存储插件,本质是对原生平台存储的封装:
- iOS:封装
NSUserDefaults - Android:封装
SharedPreferences - 桌面端(Windows/macOS):封装本地 JSON 文件
- 核心特点:轻量、API 简单、持久化(APP 重启 / 卸载前数据不丢失)、仅支持基础数据类型
二、支持的数据类型
| 类型 | 对应 API 方法 | 说明 |
|---|---|---|
| 字符串 | setString()/getString() |
存储 token、用户名等 |
| 布尔值 | setBool()/getBool() |
存储开关状态、是否登录等 |
| 整数 | setInt()/getInt() |
存储计数、ID 等 |
| 浮点数 | setDouble()/getDouble() |
存储版本号、数值配置等 |
| 字符串列表 | setStringList()/getStringList() |
存储历史记录、标签等 |
三、基础使用步骤
1. 安装依赖
在 pubspec.yaml 中添加:
yaml
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.5.4 # 推荐使用最新稳定版
2. API 使用
dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SharedPreferences 详解',
home: const SPExamplePage(),
);
}
}
class SPExamplePage extends StatefulWidget {
const SPExamplePage({super.key});
@override
State<SPExamplePage> createState() => _SPExamplePageState();
}
class _SPExamplePageState extends State<SPExamplePage> {
// 存储的测试数据
String _userToken = "";
bool _isDarkMode = false;
int _loginCount = 0;
double _appVersion = 1.0;
List<String> _historyList = [];
// 初始化:页面加载时读取存储的数据
@override
void initState() {
super.initState();
_loadAllData();
}
// ========== 核心方法1:读取数据 ==========
Future<void> _loadAllData() async {
// 1. 获取 SharedPreferences 实例(必须异步)
SharedPreferences prefs = await SharedPreferences.getInstance();
// 2. 读取数据:第二个参数是「默认值」(key不存在时返回)
setState(() {
_userToken = prefs.getString("user_token") ?? ""; // 字符串默认空
_isDarkMode = prefs.getBool("dark_mode") ?? false; // 布尔默认false
_loginCount = prefs.getInt("login_count") ?? 0; // 整数默认0
_appVersion = prefs.getDouble("app_version") ?? 1.0; // 浮点数默认1.0
_historyList = prefs.getStringList("browse_history") ?? []; // 列表默认空
});
}
// ========== 核心方法2:保存数据 ==========
Future<void> _saveAllData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// 1. 保存单个数据
await prefs.setString("user_token", "abc123456789");
await prefs.setBool("dark_mode", true);
await prefs.setInt("login_count", _loginCount + 1); // 计数+1
await prefs.setDouble("app_version", 2.1);
await prefs.setStringList("browse_history", ["首页", "我的", "设置"]);
// 2. 保存后刷新页面数据
_loadAllData();
// 提示用户
if (mounted) { // 防止页面销毁后调用context
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("数据保存成功 ✅")),
);
}
}
// ========== 核心方法3:删除数据 ==========
Future<void> _deleteData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// 方式1:删除单个key
await prefs.remove("user_token");
// 方式2:清空所有数据(谨慎使用!)
// await prefs.clear();
// 刷新数据
_loadAllData();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("数据删除成功 ❌")),
);
}
}
// ========== 核心方法4:检查key是否存在 ==========
Future<void> _checkKeyExists() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool hasToken = prefs.containsKey("user_token");
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("user_token 是否存在:$hasToken")),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("SharedPreferences 详解")),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 显示存储的数据
Text("用户Token:$_userToken"),
Text("深色模式:$_isDarkMode"),
Text("登录次数:$_loginCount"),
Text("APP版本:$_appVersion"),
Text("浏览历史:${_historyList.join(", ")}"),
const SizedBox(height: 30),
// 操作按钮
Row(
children: [
ElevatedButton(
onPressed: _saveAllData,
child: const Text("保存数据"),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: _deleteData,
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Text("删除Token"),
),
],
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: _checkKeyExists,
child: const Text("检查Token是否存在"),
),
],
),
),
);
}
}
四、进阶技巧
1. 封装工具类(避免重复代码)
实际项目中建议封装成单例工具类,统一管理存储的 key 和方法:
csharp
import 'package:shared_preferences/shared_preferences.dart';
class SPUtil {
// 单例模式
static SPUtil? _instance;
static SharedPreferences? _prefs;
// 私有化构造函数
SPUtil._();
// 获取单例
static Future<SPUtil> getInstance() async {
if (_instance == null) {
_instance = SPUtil._();
}
if (_prefs == null) {
_prefs = await SharedPreferences.getInstance();
}
return _instance!;
}
// ========== 定义存储的key(统一管理,避免拼写错误) ==========
static const String KEY_USER_TOKEN = "user_token";
static const String KEY_DARK_MODE = "dark_mode";
static const String KEY_LOGIN_COUNT = "login_count";
// ========== 封装常用方法 ==========
// 保存字符串
Future<void> setString(String key, String value) async {
await _prefs?.setString(key, value);
}
// 读取字符串
String getString(String key, {String defaultValue = ""}) {
return _prefs?.getString(key) ?? defaultValue;
}
// 保存布尔值
Future<void> setBool(String key, bool value) async {
await _prefs?.setBool(key, value);
}
// 读取布尔值
bool getBool(String key, {bool defaultValue = false}) {
return _prefs?.getBool(key) ?? defaultValue;
}
// 删除单个key
Future<void> remove(String key) async {
await _prefs?.remove(key);
}
// 清空所有数据
Future<void> clear() async {
await _prefs?.clear();
}
}
// 使用示例
void useSPUtil() async {
SPUtil spUtil = await SPUtil.getInstance();
// 保存
await spUtil.setString(SPUtil.KEY_USER_TOKEN, "123456");
// 读取
String token = spUtil.getString(SPUtil.KEY_USER_TOKEN);
print("Token:$token");
}
2. 存储复杂对象(序列化 / 反序列化)
shared_preferences 不支持直接存储对象,需先转 JSON 字符串:
dart
import 'dart:convert';
// 定义用户模型
class User {
String name;
int age;
String email;
User({required this.name, required this.age, required this.email});
// 转JSON字符串
String toJson() {
Map<String, dynamic> map = {
"name": name,
"age": age,
"email": email,
};
return json.encode(map);
}
// 从JSON字符串转对象
static User fromJson(String jsonStr) {
Map<String, dynamic> map = json.decode(jsonStr);
return User(
name: map["name"],
age: map["age"],
email: map["email"],
);
}
}
// 存储/读取对象
Future<void> saveUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// 1. 创建对象
User user = User(name: "张三", age: 25, email: "zhangsan@example.com");
// 2. 转JSON字符串保存
await prefs.setString("user_info", user.toJson());
// 3. 读取并转对象
String userJson = prefs.getString("user_info") ?? "";
if (userJson.isNotEmpty) {
User savedUser = User.fromJson(userJson);
print("用户名:${savedUser.name},年龄:${savedUser.age}");
}
}
五、避坑指南(常见问题)
1. 同步 / 异步问题(最容易踩坑)
-
❌ 错误:在
initState中同步调用getString(getInstance是异步的)typescript@override void initState() { super.initState(); // 错误!SharedPreferences.getInstance() 是异步,不能直接同步调用 String token = SharedPreferences.getInstance().then((prefs) => prefs.getString("token")); } -
✅ 正确:用
async/await或then处理异步typescript@override void initState() { super.initState(); _loadData(); // 异步方法 } Future<void> _loadData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); String token = prefs.getString("token") ?? ""; }
2. 页面销毁后调用 setState
-
问题:异步操作完成后页面已销毁,调用
setState会报错 -
解决:用
mounted判断页面是否挂载iniFuture<void> _loadData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); if (mounted) { // 关键:判断页面是否还在 setState(() { _token = prefs.getString("token") ?? ""; }); } }
3. 数据未及时刷新
-
问题:保存数据后,UI 没有实时更新
-
解决:保存后重新调用读取方法,触发
setStatecsharpawait prefs.setString("token", "new_token"); _loadData(); // 重新读取,刷新UI