Flutter本地数据持久化方案对比

对比SharedPreferences、Hive、SQLite等本地存储方案的优缺点及适用场景。

前言

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;
  }
}

优缺点

适用场景

用户设置、主题偏好、首次启动标记等简单配置。

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();
  }
}

优缺点

适用场景

用户数据、缓存数据、离线数据存储等。

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]);
  }
}

优缺点

适用场景

需要复杂查询的场景,如照片管理、聊天记录、离线数据同步等。

方案选择建议

选择SharedPreferences当:

选择Hive当:

选择SQLite当:

实际项目中的组合使用

在「回留」项目中,我采用了组合方案:

不同的数据用最适合的方案存储,发挥各自优势。

总结

没有最好的存储方案,只有最适合的。理解各方案的特点,根据实际需求选择,必要时可以组合使用。