在 Flutter 中,数据存储和使用有多种方式,根据不同的使用场景可以选择合适的方案。常见的数据存储方式有内存存储(临时数据)、本地存储(持久化数据)、SQLite(本地数据库)
1. 内存存储(临时数据)
内存存储(临时数据)指的是将数据存储在应用的运行内存(RAM)中,仅在应用运行期间有效,当应用关闭或进程终止时数据会被自动释放。这种存储方式非常适合存储临时状态、页面间传递的数据、会话信息等不需要长期保存的内容
// 使用变量存储
String username = "张三";
List<String> items = ["苹果", "香蕉", "橙子"];
// 使用Provider、Bloc等状态管理工具存储全局状态
class CounterProvider with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
2.本地存储(持久化数据)
a.文件存储
文件存储指的是将数据以文件形式保存到设备的本地存储系统(如手机的内部存储或外部存储)中,实现数据的持久化保存。这种存储方式的数据不会因应用关闭或进程终止而丢失,除非主动删除文件或清除应用数据。这种存储方式适合保存用户生成的内容或存储配置文件或自定义设置
i.依赖准备
dependencies:
path_provider: ^2.0.15 # 用于获取设备标准存储路径
ii.常用存储路径
- 应用文档目录:用于存储应用私有的、用户可能需要备份的文件(不会被系统自动清理)
Directory documentsDir = await getApplicationDocumentsDirectory();
- 临时目录:用于存储临时文件(系统可能在内存不足时清理)
Directory tempDir = await getTemporaryDirectory();
- 应用支持目录:用于存储应用运行所需的支持文件(通常用户无需直接访问)
Directory supportDir = await getApplicationSupportDirectory();
- 外部存储目录(仅 Android):用于存储可被其他应用访问的公共文件
Directory? externalDir = await getExternalStorageDirectory();
iii.文件存储示例
- 写入数据到文件
import 'dart:io';
import 'package:path_provider/path_provider.dart';
// 写入文本内容到文件
Future<void> writeToFile(String content) async {
try {
// 获取应用文档目录
final directory = await getApplicationDocumentsDirectory();
// 创建文件路径(文档目录 + 文件名)
final file = File('${directory.path}/user_notes.txt');
// 写入内容(覆盖模式)
await file.writeAsString(content);
print('数据写入成功: ${file.path}');
} catch (e) {
print('写入失败: $e');
}
}
// 调用示例
writeToFile('这是用户的第一条笔记');
- 从文件读取数据
// 从文件读取文本内容
Future<String?> readFromFile() async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/user_notes.txt');
// 判断文件是否存在
if (await file.exists()) {
String content = await file.readAsString();
print('读取成功: $content');
return content;
} else {
print('文件不存在');
return null;
}
} catch (e) {
print('读取失败: $e');
return null;
}
}
// 调用示例
String? note = await readFromFile();
- 追加数据到文件
// 向文件追加内容
Future<void> appendToFile(String content) async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/user_notes.txt');
// 追加内容(在原有内容后添加)
await file.writeAsString('\n$content', mode: FileMode.append);
print('数据追加成功');
} catch (e) {
print('追加失败: $e');
}
}
// 调用示例
appendToFile('这是追加的第二条笔记');
- 删除文件
// 删除文件
Future<void> deleteFile() async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/user_notes.txt');
if (await file.exists()) {
await file.delete();
print('文件已删除');
} else {
print('文件不存在,无需删除');
}
} catch (e) {
print('删除失败: $e');
}
}
iv.二进制文件存储
文件存储也支持二进制数据(如图片、音频等)
// 保存二进制数据(如图片)
Future<void> saveImage(Uint8List imageData) async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/profile_image.png');
// 写入二进制数据
await file.writeAsBytes(imageData);
print('图片保存成功');
} catch (e) {
print('图片保存失败: $e');
}
}
// 读取二进制数据(如图片)
Future<Uint8List?> loadImage() async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/profile_image.png');
if (await file.exists()) {
return await file.readAsBytes();
}
return null;
} catch (e) {
print('图片读取失败: $e');
return null;
}
}
v.注意
- 频繁的文件读写会影响性能,建议批量操作或使用缓存
- 文件存储的内容默认不加密,敏感数据(如 token、密码)建议加密后再存储
b.SharedPreferences
SharedPreferences 是一个轻量级的键值对存储解决方案,用于持久化存储简单数据(如用户设置、登录状态标记、偏好配置等)。它基于平台原生的存储机制实现(Android 上使用 SharedPreferences,iOS 上使用 NSUserDefaults),使用简单且跨平台兼容
i.依赖准备
dependencies:
shared_preferences: ^2.2.2 # 最新版本可查看 pub.dev
ii.操作示例
import 'package:shared_preferences/shared_preferences.dart';
// 存储数据
Future<void> saveData() async {
// 获取 SharedPreferences 实例(异步操作)
final prefs = await SharedPreferences.getInstance();
// 存储不同类型的数据
await prefs.setString('username', '张三'); // 字符串
await prefs.setInt('age', 25); // 整数
await prefs.setDouble('height', 1.75); // 双精度浮点数
await prefs.setBool('isPremium', false); // 布尔值
await prefs.setStringList('hobbies', ['阅读', '编程', '运动']); // 字符串列表
}
// 读取数据
Future<void> readData() async {
final prefs = await SharedPreferences.getInstance();
// 读取数据(注意返回值可能为 null,需处理)
String? username = prefs.getString('username');
int? age = prefs.getInt('age');
double? height = prefs.getDouble('height');
bool? isPremium = prefs.getBool('isPremium');
List<String>? hobbies = prefs.getStringList('hobbies');
// 打印读取结果
print('用户名: $username');
print('年龄: $age');
print('身高: $height');
print('是否会员: $isPremium');
print('爱好: $hobbies');
}
// 更新数据(覆盖原有值)
Future<void> updateData() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('age', 26); // 覆盖原有年龄值
await prefs.setBool('isPremium', true); // 覆盖会员状态
}
// 删除数据
Future<void> deleteData() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('height'); // 删除指定键的数据
}
// 清除所有数据
Future<void> clearAllData() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear(); // 清除所有存储的键值对
}
// 检查键是否存在
Future<void> checkKeyExists() async {
final prefs = await SharedPreferences.getInstance();
bool hasUsername = prefs.containsKey('username');
print('是否存在 username 键: $hasUsername');
}
iii.注意
- 不适合存储大量数据或频繁读写,否则可能影响应用性能
- 确保键名唯一,避免不同功能模块使用相同键名导致数据覆盖
SharedPreferences存储的数据是明文的,不适合存储敏感信息(如密码、银行卡号)。敏感数据建议加密后再存储
3.SQLite(本地数据库)
SQLite 是一种功能强大的本地关系型数据库解决方案,非常适合存储结构化数据和处理复杂的数据关系。通常使用sqflite插件来实现 SQLite 数据库操作,它提供了完整的 SQL 语法支持、事务处理和数据库管理功能。
i.依赖准备
dependencies:
sqflite: ^2.3.0 # SQLite操作核心库
path: ^1.8.3 # 用于处理文件路径
ii.代码示例
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
// 初始化数据库
Future<Database> initDatabase() async {
// 获取数据库存储路径
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'user_database.db');
// 打开数据库,如果不存在则创建
return openDatabase(
path,
version: 1,
// 数据库首次创建时执行
onCreate: (db, version) {
// 创建用户表
return db.execute(
'CREATE TABLE users('
'id INTEGER PRIMARY KEY AUTOINCREMENT, '
'name TEXT NOT NULL, '
'age INTEGER, '
'email TEXT UNIQUE, '
'created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP'
')',
);
},
// 数据库版本升级时执行
onUpgrade: (db, oldVersion, newVersion) {
// 可以在这里执行表结构更新操作
},
);
}
// 插入数据
Future<int> insertUser(Map<String, dynamic> user) async {
final db = await initDatabase();
// 插入数据并返回新插入记录的ID
return await db.insert(
'users',
user,
// 遇到冲突时替换旧数据
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// 查询所有用户
Future<List<Map<String, dynamic>>> getAllUsers() async {
final db = await initDatabase();
// 查询所有用户,按名称排序
return await db.query('users', orderBy: 'name ASC');
}
// 根据ID查询用户
Future<Map<String, dynamic>?> getUserById(int id) async {
final db = await initDatabase();
final List<Map<String, dynamic>> maps = await db.query(
'users',
where: 'id = ?',
whereArgs: [id], // 使用参数化查询防止SQL注入
);
if (maps.isNotEmpty) {
return maps.first;
}
return null;
}
// 更新用户信息
Future<int> updateUser(int id, Map<String, dynamic> updatedData) async {
final db = await initDatabase();
// 更新数据并返回受影响的行数
return await db.update(
'users',
updatedData,
where: 'id = ?',
whereArgs: [id],
);
}
// 删除用户
Future<int> deleteUser(int id) async {
final db = await initDatabase();
// 删除数据并返回受影响的行数
return await db.delete(
'users',
where: 'id = ?',
whereArgs: [id],
);
}
// 使用事务处理多个操作
Future<void> batchOperation() async {
final db = await initDatabase();
// 开启事务
await db.transaction((txn) async {
// 在事务中执行多个操作
await txn.insert('users', {'name': '张三', 'age': 25, 'email': 'zhangsan@example.com'});
await txn.insert('users', {'name': '李四', 'age': 30, 'email': 'lisi@example.com'});
await txn.update('users', {'age': 26}, where: 'name = ?', whereArgs: ['张三']);
});
}
// 调用示例
void main() async {
// 插入用户
int userId = await insertUser({
'name': '王五',
'age': 28,
'email': 'wangwu@example.com',
});
print('插入的用户ID: $userId');
// 查询所有用户
List<Map<String, dynamic>> users = await getAllUsers();
print('所有用户: $users');
// 更新用户
int updatedRows = await updateUser(userId, {'age': 29});
print('更新的行数: $updatedRows');
// 事务操作
await batchOperation();
// 删除用户
int deletedRows = await deleteUser(userId);
print('删除的行数: $deletedRows');
}
iii.注意
sqflite默认不保证线程安全,多线程操作需注意同步