Flutter本地存储
当我们关闭App再重新打开,为什么有些数据(比如登录状态、用户设置、文章草稿)还在,而有些数据(比如临时弹窗状态)却消失了?这背后就是 "本地存储" 在发挥作用。可以说,一个不懂得如何管理本地数据的开发者,很难做出用户体验好的应用。
今天,我们就来彻底搞懂Flutter中的本地存储。
一、 为什么需要本地存储?
举个例子:如果你每天醒来都会失忆,不记得自己的名字、家在哪里、昨天做了什么......这简直是一场灾难。对于App而言,本地存储就是它的 "记忆系统"。
主要应用场景:
- 用户偏好设置:比如主题颜色、语言选择、消息提醒开关。
- 登录状态保持:用户登录后,App"记住"他,下次打开无需重新登录。
- 缓存网络数据:将首屏数据缓存下来,下次启动秒开,提升用户体验。
- 离线数据持久化:如笔记草稿、阅读进度、购物车商品,即使断网也不丢失。
- 大数据量结构化存储:比如聊天记录、交易明细等。
Flutter拥有多种本地数据存储方案,下面我们先看下用张图来了解下存储方案脉络:
二、 shared_preferences
2.1 它是什么?能干什么?
shared_preferences 这个名字听起来有点拗口,但其实很简单。你可以把它理解成 Flutter 为我们在本地提供的一个 "小本子",专门用来记录一些简单的、键值对形式的数据。
shared(共享):指这些数据在你的App内是共享的,任何页面都能读写。preferences(偏好):顾名思义,最适合存储用户的偏好设置。
它的本质是什么? 在 Android 上,它背后是通过 SharedPreferences API 将数据以 XML 文件形式存储;在 iOS 上,则使用的是 NSUserDefaults。Flutter 插件帮我们统一了这两端的接口,让我们可以用一套代码搞定双端存储。
2.2 工作原理图解
让我们看看当你调用 setString('name', '一一') 时,背后发生了什么:
关键点:
- 异步操作 :所有读写操作都是
Future,意味着不会阻塞你的UI线程。 - 持久化:数据被写入设备文件系统,App重启后依然存在。
- 平台差异被屏蔽:你不需要关心底层是XML还是plist,插件帮你处理了。
2.3 下面用一段代码来详细介绍下
第一步:引入依赖 在 pubspec.yaml 文件中添加:
yaml
dependencies:
shared_preferences: ^2.2.2 # 请使用最新版本
然后运行 flutter pub get。
第二步:基础CRUD操作
dart
import 'package:shared_preferences/shared_preferences.dart';
class SPManager {
// 单例
static final SPManager _instance = SPManager._internal();
factory SPManager() => _instance;
SPManager._internal();
late SharedPreferences _prefs;
// 初始化
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
print('SharedPreferences 初始化完成!');
}
// 1. 写入数据
Future<bool> saveUserInfo() async {
try {
// 字符串
await _prefs.setString('username', 'Flutter本地存储');
// 整型
await _prefs.setInt('userAge', 28);
// 布尔值
await _prefs.setBool('isVip', true);
// 字符串列表
await _prefs.setStringList('hobbies', ['编程', '读书', '健身']);
// 双精度浮点数
await _prefs.setDouble('walletBalance', 99.99);
print('用户信息保存成功!');
return true;
} catch (e) {
print('保存失败: $e');
return false;
}
}
// 2. 读取数据
void readUserInfo() {
// 读取字符串,提供默认值
String username = _prefs.getString('username') ?? '未知用户';
int age = _prefs.getInt('userAge') ?? 0;
bool isVip = _prefs.getBool('isVip') ?? false;
double balance = _prefs.getDouble('walletBalance') ?? 0.0;
List<String> hobbies = _prefs.getStringList('hobbies') ?? [];
print('''
用户信息:
用户名:$username
年龄:$age
VIP:$isVip
余额:$balance
爱好:$hobbies
''');
}
// 3. 删除数据
Future<bool> deleteUserInfo() async {
try {
// 删除指定键
await _prefs.remove('username');
// 清空所有数据
// await _prefs.clear();
print('用户信息已删除');
return true;
} catch (e) {
print('删除失败: $e');
return false;
}
}
// 4. 检查键是否存在
bool containsKey(String key) {
return _prefs.containsKey(key);
}
// 5. 获取所有键
Set<String> getAllKeys() {
return _prefs.getKeys();
}
}
第三步:在App中使用
dart
void main() async {
// 确保WidgetsBinding初始化
WidgetsFlutterBinding.ensureInitialized();
// 初始化SPManager
await SPManager().init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final SPManager _spManager = SPManager();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('SP演示')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => _spManager.saveUserInfo(),
child: Text('保存用户信息'),
),
ElevatedButton(
onPressed: () => _spManager.readUserInfo(),
child: Text('读取用户信息'),
),
ElevatedButton(
onPressed: () => _spManager.deleteUserInfo(),
child: Text('删除用户信息'),
),
],
),
),
);
}
}
2.4 使用介绍与注意点
-
一定要先初始化 :在使用前必须调用
getInstance()并等待其完成。 -
处理空值:读取时一定要提供默认值,因为键可能不存在。
-
不要存大数据:它不适合存储大型对象或列表,性能会变差。
-
键名管理 :建议使用常量来管理键名,避免拼写错误。
dartclass SPKeys { static const String username = 'username'; static const String userAge = 'user_age'; static const String isVip = 'is_vip'; } -
异步错误处理 :使用
try-catch包裹可能出错的操作。
三、 文件存储
3.1 适用场景
当你的数据不适合用键值对存储时,文件存储就派上用场了:
- App的配置文件(JSON, XML)
- 用户下载的图片、文档
- 应用日志文件
- 需要自定义格式的数据
3.2 文件系统路径详解
在Flutter中,我们使用 path_provider 插件来获取各种路径:
dart
import 'package:path_provider/path_provider.dart';
class FilePathManager {
// 获取临时目录
static Future<String> get tempPath async {
final dir = await getTemporaryDirectory();
return dir.path;
}
// 获取文档目录(Android对应App专用目录,iOS对应Documents)
static Future<String> get documentsPath async {
final dir = await getApplicationDocumentsDirectory();
return dir.path;
}
// 获取外部存储目录
static Future<String?> get externalStoragePath async {
final dir = await getExternalStorageDirectory();
return dir?.path;
}
// 获取支持目录
static Future<String> get supportPath async {
final dir = await getApplicationSupportDirectory();
return dir.path;
}
}
路径选择:
- 临时文件 :
getTemporaryDirectory()- 缓存,系统可清理 - 用户数据 :
getApplicationDocumentsDirectory()- 用户生成的内容 - App内部文件 :
getApplicationSupportDirectory()- App运行所需文件
3.3 文件存储实战演示
一个完整的文件管理类如下代码所示:
dart
import 'dart:io';
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
class FileManager {
// 单例
static final FileManager _instance = FileManager._internal();
factory FileManager() => _instance;
FileManager._internal();
// 获取文件路径
Future<String> _getLocalFilePath(String filename) async {
final dir = await getApplicationDocumentsDirectory();
return '${dir.path}/$filename';
}
// 1. 写入字符串到文件
Future<File> writeStringToFile(String content, String filename) async {
try {
final file = File(await _getLocalFilePath(filename));
return await file.writeAsString(content);
} catch (e) {
print('写入文件失败: $e');
rethrow;
}
}
// 2. 从文件读取字符串
Future<String> readStringFromFile(String filename) async {
try {
final file = File(await _getLocalFilePath(filename));
if (await file.exists()) {
return await file.readAsString();
} else {
throw Exception('文件不存在');
}
} catch (e) {
print('读取文件失败: $e');
rethrow;
}
}
// 3. 写入JSON对象
Future<File> writeJsonToFile(Map<String, dynamic> json, String filename) async {
final jsonString = jsonEncode(json);
return await writeStringToFile(jsonString, filename);
}
// 4. 从文件读取JSON对象
Future<Map<String, dynamic>> readJsonFromFile(String filename) async {
try {
final jsonString = await readStringFromFile(filename);
return jsonDecode(jsonString);
} catch (e) {
print('读取JSON失败: $e');
rethrow;
}
}
// 5. 增加内容到文件
Future<File> appendToFile(String content, String filename) async {
try {
final file = File(await _getLocalFilePath(filename));
return await file.writeAsString(content, mode: FileMode.append);
} catch (e) {
print('追加文件失败: $e');
rethrow;
}
}
// 6. 检查文件是否存在
Future<bool> fileExists(String filename) async {
final file = File(await _getLocalFilePath(filename));
return await file.exists();
}
// 7. 删除文件
Future<void> deleteFile(String filename) async {
try {
final file = File(await _getLocalFilePath(filename));
if (await file.exists()) {
await file.delete();
print('文件删除成功: $filename');
}
} catch (e) {
print('删除文件失败: $e');
rethrow;
}
}
// 8. 获取文件信息
Future<FileStat> getFileInfo(String filename) async {
try {
final file = File(await _getLocalFilePath(filename));
if (await file.exists()) {
return await file.stat();
} else {
throw Exception('文件不存在');
}
} catch (e) {
print('获取文件信息失败: $e');
rethrow;
}
}
}
3.4 以用户配置管理为例:
dart
class UserConfigManager {
static const String _configFileName = 'user_config.json';
final FileManager _fileManager = FileManager();
// 保存用户配置
Future<void> saveUserConfig({
required String theme,
required String language,
required bool darkMode,
required List<String> recentSearches,
}) async {
final config = {
'theme': theme,
'language': language,
'darkMode': darkMode,
'recentSearches': recentSearches,
'lastUpdated': DateTime.now().toIso8601String(),
};
await _fileManager.writeJsonToFile(config, _configFileName);
print('用户配置已保存');
}
// 读取用户配置
Future<Map<String, dynamic>> loadUserConfig() async {
try {
if (await _fileManager.fileExists(_configFileName)) {
return await _fileManager.readJsonFromFile(_configFileName);
} else {
// 返回默认配置
return _getDefaultConfig();
}
} catch (e) {
print('加载用户配置失败,使用默认配置: $e');
return _getDefaultConfig();
}
}
Map<String, dynamic> _getDefaultConfig() {
return {
'theme': 'light',
'language': 'zh-CN',
'darkMode': false,
'recentSearches': [],
'lastUpdated': DateTime.now().toIso8601String(),
};
}
// 清空配置
Future<void> clearConfig() async {
await _fileManager.deleteFile(_configFileName);
}
}
四、 SQLite
4.1 什么是SQLite?为什么需要它?
SQLite是一个轻量级的、文件式的关系型数据库。它不需要单独的服务器进程,整个数据库就是一个文件,非常适合移动端应用。
使用场景:
- 用户关系管理(联系人、好友)
- 商品目录、订单管理
- 聊天消息记录
- 任何需要复杂查询和关系的数据
4.2 Flutter中的SQLite架构
在Flutter中,我们通常使用 sqflite 插件来操作SQLite:
4.3 构建一个任务管理App
第一步:添加依赖
yaml
dependencies:
sqflite: ^2.3.0
path: ^1.8.3
第二步:创建数据库工具类
dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static final DatabaseHelper _instance = DatabaseHelper._internal();
factory DatabaseHelper() => _instance;
DatabaseHelper._internal();
static Database? _database;
// 数据库名称和版本
static const String _dbName = 'task_manager.db';
static const int _dbVersion = 1;
// 表名和列名
static const String tableTasks = 'tasks';
static const String columnId = 'id';
static const String columnTitle = 'title';
static const String columnDescription = 'description';
static const String columnIsCompleted = 'is_completed';
static const String columnCreatedAt = 'created_at';
static const String columnUpdatedAt = 'updated_at';
// 获取数据库实例
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
// 初始化数据库
Future<Database> _initDatabase() async {
// 获取数据库路径
String path = join(await getDatabasesPath(), _dbName);
// 创建/打开数据库
return await openDatabase(
path,
version: _dbVersion,
onCreate: _createTables,
onUpgrade: _upgradeDatabase,
);
}
// 创建表
Future<void> _createTables(Database db, int version) async {
await db.execute('''
CREATE TABLE $tableTasks (
$columnId INTEGER PRIMARY KEY AUTOINCREMENT,
$columnTitle TEXT NOT NULL,
$columnDescription TEXT,
$columnIsCompleted INTEGER NOT NULL DEFAULT 0,
$columnCreatedAt INTEGER NOT NULL,
$columnUpdatedAt INTEGER NOT NULL
)
''');
print('任务表创建成功!');
}
// 数据库升级
Future<void> _upgradeDatabase(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
// await db.execute('ALTER TABLE $tableTasks ADD COLUMN new_column TEXT');
}
print('数据库从版本 $oldVersion 升级到 $newVersion');
}
// 关闭数据库
Future<void> close() async {
if (_database != null) {
await _database!.close();
_database = null;
}
}
}
第三步:创建数据模型
dart
class Task {
int? id;
String title;
String? description;
bool isCompleted;
DateTime createdAt;
DateTime updatedAt;
Task({
this.id,
required this.title,
this.description,
this.isCompleted = false,
DateTime? createdAt,
DateTime? updatedAt,
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
// 将Task对象转换为Map,便于存入数据库
Map<String, dynamic> toMap() {
return {
DatabaseHelper.columnId: id,
DatabaseHelper.columnTitle: title,
DatabaseHelper.columnDescription: description,
DatabaseHelper.columnIsCompleted: isCompleted ? 1 : 0,
DatabaseHelper.columnCreatedAt: createdAt.millisecondsSinceEpoch,
DatabaseHelper.columnUpdatedAt: updatedAt.millisecondsSinceEpoch,
};
}
// 从Map创建Task对象
factory Task.fromMap(Map<String, dynamic> map) {
return Task(
id: map[DatabaseHelper.columnId],
title: map[DatabaseHelper.columnTitle],
description: map[DatabaseHelper.columnDescription],
isCompleted: map[DatabaseHelper.columnIsCompleted] == 1,
createdAt: DateTime.fromMillisecondsSinceEpoch(
map[DatabaseHelper.columnCreatedAt]),
updatedAt: DateTime.fromMillisecondsSinceEpoch(
map[DatabaseHelper.columnUpdatedAt]),
);
}
@override
String toString() {
return 'Task{id: $id, title: $title, completed: $isCompleted}';
}
}
第四步:创建数据访问对象
dart
class TaskDao {
final DatabaseHelper _dbHelper = DatabaseHelper();
// 1. 插入新任务
Future<int> insertTask(Task task) async {
final db = await _dbHelper.database;
// 更新时间戳
task.updatedAt = DateTime.now();
final id = await db.insert(
DatabaseHelper.tableTasks,
task.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
print('任务创建成功,ID: $id');
return id;
}
// 2. 根据ID查询任务
Future<Task?> getTaskById(int id) async {
final db = await _dbHelper.database;
final maps = await db.query(
DatabaseHelper.tableTasks,
where: '${DatabaseHelper.columnId} = ?',
whereArgs: [id],
);
if (maps.isNotEmpty) {
return Task.fromMap(maps.first);
}
return null;
}
// 3. 查询所有任务
Future<List<Task>> getAllTasks() async {
final db = await _dbHelper.database;
final maps = await db.query(
DatabaseHelper.tableTasks,
orderBy: '${DatabaseHelper.columnCreatedAt} DESC',
);
return maps.map((map) => Task.fromMap(map)).toList();
}
// 4. 查询未完成的任务
Future<List<Task>> getIncompleteTasks() async {
final db = await _dbHelper.database;
final maps = await db.query(
DatabaseHelper.tableTasks,
where: '${DatabaseHelper.columnIsCompleted} = ?',
whereArgs: [0],
orderBy: '${DatabaseHelper.columnCreatedAt} DESC',
);
return maps.map((map) => Task.fromMap(map)).toList();
}
// 5. 更新任务
Future<int> updateTask(Task task) async {
final db = await _dbHelper.database;
// 更新修改时间
task.updatedAt = DateTime.now();
final count = await db.update(
DatabaseHelper.tableTasks,
task.toMap(),
where: '${DatabaseHelper.columnId} = ?',
whereArgs: [task.id],
);
if (count > 0) {
print('任务更新成功: ${task.title}');
}
return count;
}
// 6. 删除任务
Future<int> deleteTask(int id) async {
final db = await _dbHelper.database;
final count = await db.delete(
DatabaseHelper.tableTasks,
where: '${DatabaseHelper.columnId} = ?',
whereArgs: [id],
);
if (count > 0) {
print('任务删除成功, ID: $id');
}
return count;
}
// 7. 批量操作
Future<void> batchInsertTasks(List<Task> tasks) async {
final db = await _dbHelper.database;
final batch = db.batch();
for (final task in tasks) {
batch.insert(DatabaseHelper.tableTasks, task.toMap());
}
await batch.commit();
print('批量插入 ${tasks.length} 个任务成功');
}
// 8. 复杂查询:搜索任务
Future<List<Task>> searchTasks(String keyword) async {
final db = await _dbHelper.database;
final maps = await db.query(
DatabaseHelper.tableTasks,
where: '''
${DatabaseHelper.columnTitle} LIKE ? OR
${DatabaseHelper.columnDescription} LIKE ?
''',
whereArgs: ['%$keyword%', '%$keyword%'],
orderBy: '${DatabaseHelper.columnCreatedAt} DESC',
);
return maps.map((map) => Task.fromMap(map)).toList();
}
// 9. 事务操作
Future<void> markAllAsCompleted() async {
final db = await _dbHelper.database;
await db.transaction((txn) async {
await txn.update(
DatabaseHelper.tableTasks,
{
DatabaseHelper.columnIsCompleted: 1,
DatabaseHelper.columnUpdatedAt: DateTime.now().millisecondsSinceEpoch,
},
);
});
print('所有任务标记为完成');
}
}
第五步:在UI中使用
dart
class TaskListPage extends StatefulWidget {
@override
_TaskListPageState createState() => _TaskListPageState();
}
class _TaskListPageState extends State<TaskListPage> {
final TaskDao _taskDao = TaskDao();
List<Task> _tasks = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadTasks();
}
Future<void> _loadTasks() async {
setState(() => _isLoading = true);
try {
final tasks = await _taskDao.getAllTasks();
setState(() => _tasks = tasks);
} catch (e) {
print('加载任务失败: $e');
// 错误提示
} finally {
setState(() => _isLoading = false);
}
}
Future<void> _addTask() async {
final newTask = Task(
title: '新任务 ${DateTime.now().second}',
description: '这是一个新任务的描述',
);
await _taskDao.insertTask(newTask);
await _loadTasks(); // 重新加载列表
}
Future<void> _toggleTaskCompletion(Task task) async {
task.isCompleted = !task.isCompleted;
await _taskDao.updateTask(task);
await _loadTasks();
}
Future<void> _deleteTask(Task task) async {
if (task.id != null) {
await _taskDao.deleteTask(task.id!);
await _loadTasks();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('任务管理器 (${_tasks.length})'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: _addTask,
),
],
),
body: _isLoading
? Center(child: CircularProgressIndicator())
: _tasks.isEmpty
? Center(child: Text('还没有任务,点击+号添加吧!'))
: ListView.builder(
itemCount: _tasks.length,
itemBuilder: (context, index) {
final task = _tasks[index];
return Dismissible(
key: Key(task.id.toString()),
background: Container(color: Colors.red),
onDismissed: (_) => _deleteTask(task),
child: ListTile(
leading: Checkbox(
value: task.isCompleted,
onChanged: (_) => _toggleTaskCompletion(task),
),
title: Text(
task.title,
style: TextStyle(
decoration: task.isCompleted
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
subtitle: Text(
task.description ?? '暂无描述',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: Text(
DateFormat('MM-dd HH:mm').format(task.createdAt),
style: TextStyle(fontSize: 12, color: Colors.grey),
),
),
);
},
),
);
}
}
五、 性能优化
5.1 数据库迁移
当你的数据结构需要变更时,就需要数据库迁移:
dart
class DatabaseHelper {
static const int _dbVersion = 2; // 版本升级
Future<void> _upgradeDatabase(Database db, int oldVersion, int newVersion) async {
for (int version = oldVersion + 1; version <= newVersion; version++) {
switch (version) {
case 2:
await _migrateToV2(db);
break;
case 3:
await _migrateToV3(db);
break;
}
}
}
Future<void> _migrateToV2(Database db) async {
// 添加优先级字段
await db.execute('''
ALTER TABLE ${DatabaseHelper.tableTasks}
ADD COLUMN priority INTEGER NOT NULL DEFAULT 0
''');
print('数据库迁移到版本2成功');
}
Future<void> _migrateToV3(Database db) async {
// 创建新表或更复杂的迁移
await db.execute('''
CREATE TABLE categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
color TEXT NOT NULL
)
''');
print('数据库迁移到版本3成功');
}
}
5.2 使用ORM简化操作
虽然直接使用SQL很强大,但ORM可以让代码更简洁。推荐 floor 或 moor:
yaml
dependencies:
floor: ^1.4.0
sqflite: ^2.0.0
5.3 优化技巧
- 使用索引:对经常查询的字段创建索引
- 批量操作 :使用
batch()进行批量插入/更新 - 连接池:保持数据库连接,避免频繁开关
- 分页查询 :大数据集使用
LIMIT和OFFSET - 避免N+1查询 :使用
JOIN一次性获取关联数据
dart
// 分页查询
Future<List<Task>> getTasksPaginated(int page, int pageSize) async {
final db = await _dbHelper.database;
final offset = (page - 1) * pageSize;
final maps = await db.query(
DatabaseHelper.tableTasks,
limit: pageSize,
offset: offset,
orderBy: '${DatabaseHelper.columnCreatedAt} DESC',
);
return maps.map((map) => Task.fromMap(map)).toList();
}
六、 方案对比
下面我们通过多维度对以上几种本地存储方案进行一个详细对比:
| 维度 | shared_preferences | 文件存储 | SQLite | Hive |
|---|---|---|---|---|
| 数据类型 | 基本类型 | 任意数据 | 结构化数据 | 任意对象 |
| 查询能力 | 键值查询 | 顺序读取 | 复杂SQL查询 | 键值+条件查询 |
| 性能 | 快 | 中等 | 快(有索引) | 非常快 |
| 复杂度 | 简单 | 中等 | 复杂 | 简单 |
| 数据量 | 小(<1MB) | 中等 | 大 | 大 |
| 是否需要序列化 | 需要 | 需要 | 需要 | 不需要 |
实际项目开发中,我们如何选择本地存储?可按下面策略进行存储方案选型:
以上选型策略概述以下:
- 用户设置、登录令牌 →
shared_preferences - App配置、日志文件 → 文件存储
- 聊天记录、商品目录 → SQLite
- 缓存数据、临时状态 → Hive
- 需要极致性能 → Hive
- 需要复杂关系查询 → SQLite
七、 综合应用代码实战
下面我们构建一个完整的用户数据管理方案,综合运用多种存储方式:
dart
class UserDataManager {
final SPManager _spManager = SPManager();
final FileManager _fileManager = FileManager();
final TaskDao _taskDao = TaskDao();
// 1. 用户登录状态 - 使用shared_preferences
Future<void> saveLoginState(User user) async {
await _spManager.init();
await _spManager._prefs.setString('user_id', user.id);
await _spManager._prefs.setString('user_token', user.token);
await _spManager._prefs.setBool('is_logged_in', true);
// 同时保存用户信息到SQLite
// await _userDao.insertUser(user);
}
// 2. 用户偏好设置 - 使用文件存储
Future<void> saveUserPreferences(UserPreferences prefs) async {
await _fileManager.writeJsonToFile(
prefs.toJson(),
'user_preferences.json'
);
}
// 3. 用户任务数据 - 使用SQLite
Future<void> syncUserTasks(List<Task> tasks) async {
await _taskDao.batchInsertTasks(tasks);
}
// 4. 清理所有用户数据
Future<void> clearAllUserData() async {
// 清理SP
await _spManager._prefs.clear();
// 清理配置文件
await _fileManager.deleteFile('user_preferences.json');
// 清理数据库
// await _taskDao.deleteAllTasks();
}
}
写在最后
至此本地数据存储的知识就全学完了,记住这几个核心要点:
- 性能意识:大数据量时考虑索引、分页、批量操作
- 错误处理:存储操作可能失败,一定要有完善的错误处理
- 数据安全:敏感信息考虑加密存储
- 测试验证:数据库迁移等复杂操作要充分测试
本地存储是App开发的基础,掌握好它,就能开发出体验流畅、数据可靠的应用。希望这篇文章能帮助到你! 我们下期再见!
