前言
Flutter应用开发中,本地数据持久化是一个常见需求。不同的存储方案有各自的特点和适用场景,选择合适的方案可以让开发事半功倍。
常见存储方案概览
| 方案 | 类型 | 适用场景 | 性能 |
|---|---|---|---|
| SharedPreferences | 键值对 | 简单配置 | 一般 |
| Hive | NoSQL | 结构化数据 | 快 |
| SQLite | 关系型 | 复杂查询 | 快 |
| 文件存储 | 文件 | 大文件/JSON | 取决于文件大小 |
SharedPreferences
最简单的键值对存储方案,适合存储少量简单数据。
使用示例
import 'package:shared_preferences/shared_preferences.dart';
class SettingsService {
static late SharedPreferences _prefs;
static Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
// 存储
static Future<void> setThemeMode(String mode) async {
await _prefs.setString('theme_mode', mode);
}
// 读取
static String getThemeMode() {
return _prefs.getString('theme_mode') ?? 'system';
}
// 存储布尔值
static Future<void> setFirstLaunch(bool value) async {
await _prefs.setBool('first_launch', value);
}
// 读取布尔值
static bool isFirstLaunch() {
return _prefs.getBool('first_launch') ?? true;
}
}
优缺点
- ✅ 使用简单,无需额外配置
- ✅ 跨平台支持好
- ❌ 只支持基本类型(String、int、double、bool、List<String>)
- ❌ 不适合存储大量数据
- ❌ 没有加密功能
适用场景
用户设置、主题偏好、首次启动标记等简单配置。
Hive
轻量级、高性能的NoSQL数据库,纯Dart实现。
初始化配置
import 'package:hive_flutter/hive_flutter.dart';
Future<void> main() async {
await Hive.initFlutter();
// 注册适配器
Hive.registerAdapter(UserAdapter());
// 打开Box
await Hive.openBox<User>('users');
runApp(const MyApp());
}
定义数据模型
import 'package:hive/hive.dart';
part 'user.g.dart';
@HiveType(typeId: 0)
class User extends HiveObject {
@HiveField(0)
late String id;
@HiveField(1)
late String name;
@HiveField(2)
late String email;
@HiveField(3)
DateTime? createdAt;
}
CRUD操作
class UserRepository {
final Box<User> _box = Hive.box<User>('users');
// 创建
Future<void> addUser(User user) async {
await _box.put(user.id, user);
}
// 读取
User? getUser(String id) {
return _box.get(id);
}
// 获取所有
List<User> getAllUsers() {
return _box.values.toList();
}
// 更新
Future<void> updateUser(User user) async {
await user.save(); // HiveObject提供的方法
}
// 删除
Future<void> deleteUser(String id) async {
await _box.delete(id);
}
// 监听变化
Stream<BoxEvent> watchChanges() {
return _box.watch();
}
}
优缺点
- ✅ 性能优秀,比SharedPreferences快很多
- ✅ 支持自定义对象存储
- ✅ 支持加密
- ✅ 纯Dart实现,跨平台一致
- ❌ 需要代码生成(build_runner)
- ❌ 不支持复杂查询
适用场景
用户数据、缓存数据、离线数据存储等。
SQLite (sqflite)
成熟的关系型数据库,支持复杂SQL查询。
数据库初始化
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static Database? _database;
static Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
static Future<Database> _initDatabase() async {
final path = join(await getDatabasesPath(), 'app.db');
return await openDatabase(
path,
version: 1,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE photos (
id TEXT PRIMARY KEY,
path TEXT NOT NULL,
hash TEXT,
created_at INTEGER,
is_favorite INTEGER DEFAULT 0
)
''');
await db.execute('''
CREATE INDEX idx_photos_hash ON photos(hash)
''');
},
onUpgrade: (db, oldVersion, newVersion) async {
// 数据库升级逻辑
},
);
}
}
CRUD操作
class PhotoRepository {
// 插入
Future<void> insert(Photo photo) async {
final db = await DatabaseHelper.database;
await db.insert(
'photos',
photo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// 批量插入
Future<void> insertBatch(List<Photo> photos) async {
final db = await DatabaseHelper.database;
final batch = db.batch();
for (final photo in photos) {
batch.insert('photos', photo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace);
}
await batch.commit(noResult: true);
}
// 查询
Future<List<Photo>> getAll() async {
final db = await DatabaseHelper.database;
final maps = await db.query('photos', orderBy: 'created_at DESC');
return maps.map((m) => Photo.fromMap(m)).toList();
}
// 条件查询
Future<List<Photo>> getFavorites() async {
final db = await DatabaseHelper.database;
final maps = await db.query(
'photos',
where: 'is_favorite = ?',
whereArgs: [1],
);
return maps.map((m) => Photo.fromMap(m)).toList();
}
// 复杂查询:查找相同hash的照片
Future<List<List<Photo>>> findDuplicates() async {
final db = await DatabaseHelper.database;
final maps = await db.rawQuery('''
SELECT * FROM photos
WHERE hash IN (
SELECT hash FROM photos
GROUP BY hash
HAVING COUNT(*) > 1
)
ORDER BY hash, created_at
''');
// 按hash分组
final grouped = <String, List<Photo>>{};
for (final map in maps) {
final photo = Photo.fromMap(map);
grouped.putIfAbsent(photo.hash!, () => []).add(photo);
}
return grouped.values.toList();
}
// 删除
Future<void> delete(String id) async {
final db = await DatabaseHelper.database;
await db.delete('photos', where: 'id = ?', whereArgs: [id]);
}
}
优缺点
- ✅ 支持复杂SQL查询
- ✅ 事务支持
- ✅ 成熟稳定
- ✅ 适合大量结构化数据
- ❌ 需要手写SQL或使用ORM
- ❌ Web平台支持有限
适用场景
需要复杂查询的场景,如照片管理、聊天记录、离线数据同步等。
方案选择建议
选择SharedPreferences当:
- 只需存储简单的配置项
- 数据量很小(几十个键值对)
- 不需要复杂的数据结构
选择Hive当:
- 需要存储自定义对象
- 追求高性能
- 不需要复杂查询
- 需要跨平台一致性(包括Web)
选择SQLite当:
- 数据关系复杂
- 需要复杂的查询和聚合
- 数据量大且需要索引优化
- 需要事务支持
实际项目中的组合使用
在「回留」项目中,我采用了组合方案:
- SharedPreferences:用户设置、主题偏好
- SQLite:照片元数据、哈希值索引
- 文件缓存:缩略图缓存
不同的数据用最适合的方案存储,发挥各自优势。
总结
没有最好的存储方案,只有最适合的。理解各方案的特点,根据实际需求选择,必要时可以组合使用。