Flutter 本地存储实战:SharedPreferences+Hive+SQLite

导语

在 Flutter 开发中,本地数据持久化是核心需求之一。选择合适的存储方案并遵循最佳实践,不仅能提升应用性能,还能增强代码的可维护性和可测试性。本文系统对比 SharedPreferences、Hive 和 SQLite 三种主流存储方案,从基础封装到架构设计,带你构建健壮的本地存储系统。

一、SharedPreferences:轻量级键值存储优化

SharedPreferences 适合存储简单的键值对数据(如用户配置、Token 等),以下是企业级封装方案:

dart

复制代码
// lib/core/storage/shared_preferences_service.dart
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/foundation.dart';

/// 轻量级键值存储服务
/// 适用于:用户偏好设置、认证Token、简单配置项
class SharedPreferencesService {
  static SharedPreferences? _instance;
  static bool _isInitialized = false;

  /// 初始化服务(建议在main函数中调用)
  static Future<void> initialize() async {
    if (!_isInitialized) {
      _instance = await SharedPreferences.getInstance();
      _isInitialized = true;
    }
  }

  /// 获取实例(确保已初始化)
  static SharedPreferences get instance {
    assert(_isInitialized, 'SharedPreferencesService未初始化,请先调用initialize()');
    return _instance!;
  }

  // MARK: - 通用操作方法
  static Future<bool> setValue<T>(String key, T value) async {
    try {
      switch (T) {
        case String:
          return await instance.setString(key, value as String);
        case int:
          return await instance.setInt(key, value as int);
        case bool:
          return await instance.setBool(key, value as bool);
        case double:
          return await instance.setDouble(key, value as double);
        case List<String>:
          return await instance.setStringList(key, value as List<String>);
        default:
          throw UnsupportedError('不支持的类型: ${T.runtimeType}');
      }
    } catch (e) {
      debugPrint('SharedPreferences存储失败: $key - $e');
      return false;
    }
  }

  static T? getValue<T>(String key, {T? defaultValue}) {
    try {
      switch (T) {
        case String:
          return instance.getString(key) as T? ?? defaultValue;
        case int:
          return instance.getInt(key) as T? ?? defaultValue;
        case bool:
          return instance.getBool(key) as T? ?? defaultValue;
        case double:
          return instance.getDouble(key) as T? ?? defaultValue;
        case List<String>:
          return instance.getStringList(key) as T? ?? defaultValue;
        default:
          return defaultValue;
      }
    } catch (e) {
      debugPrint('SharedPreferences读取失败: $key - $e');
      return defaultValue;
    }
  }

  // MARK: - 便捷方法
  static Future<bool> remove(String key) async => await instance.remove(key);
  
  static Future<bool> clear() async => await instance.clear();
  
  static bool containsKey(String key) => instance.containsKey(key);

  // MARK: - 类型安全的命名键
  static const String kAuthToken = 'auth_token';
  static const String kThemeMode = 'theme_mode';
  static const String kFirstLaunch = 'first_launch';
}

使用示例

dart

复制代码
// main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化存储服务
  await SharedPreferencesService.initialize();
  
  // 安全存储Token
  await SharedPreferencesService.setValue(
    SharedPreferencesService.kAuthToken, 
    'your_jwt_token_here'
  );
  
  // 读取Token
  final token = SharedPreferencesService.getValue<String>(
    SharedPreferencesService.kAuthToken
  );
  
  runApp(const MyApp());
}
二、Hive:高性能 NoSQL 存储方案

Hive 适合存储复杂对象和本地缓存,以下是分层架构的实现方案:

1. 实体模型设计

dart

复制代码
// lib/features/notes/data/models/note_model.dart
import 'package:hive/hive.dart';
import 'package:equatable/equatable.dart';

part 'note_model.g.dart';

/// 笔记数据模型
/// Hive TypeId: 0
@HiveType(typeId: 0)
class NoteModel extends HiveObject with EquatableMixin {
  @HiveField(0)
  final String id;

  @HiveField(1)
  String title;

  @HiveField(2)
  String content;

  @HiveField(3)
  final DateTime createdAt;

  @HiveField(4)
  DateTime updatedAt;

  @HiveField(5)
  final List<String> tags;

  NoteModel({
    required this.id,
    required this.title,
    required this.content,
    required this.createdAt,
    required this.updatedAt,
    this.tags = const [],
  });

  // 更新笔记
  NoteModel copyWith({
    String? title,
    String? content,
    DateTime? updatedAt,
    List<String>? tags,
  }) {
    return NoteModel(
      id: id,
      title: title ?? this.title,
      content: content ?? this.content,
      createdAt: createdAt,
      updatedAt: updatedAt ?? DateTime.now(),
      tags: tags ?? this.tags,
    );
  }

  @override
  List<Object?> get props => [id, title, content, createdAt, updatedAt, tags];
}
2. Hive 服务封装

dart

复制代码
// lib/core/storage/hive_service.dart
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:flutter/foundation.dart';

/// Hive存储服务
/// 负责初始化、配置和提供Box实例
class HiveService {
  static const String kEncryptionKey = 'your_secure_encryption_key'; // 实际项目中使用安全方式存储
  
  /// 初始化Hive
  static Future<void> initialize() async {
    await Hive.initFlutter();
    
    // 注册适配器(代码生成)
    _registerAdapters();
    
    // 打开常用Box(可配置加密)
    await _openBoxes();
  }

  /// 注册所有Hive对象适配器
  static void _registerAdapters() {
    Hive.registerAdapter(NoteModelAdapter());
    // 注册其他适配器...
  }

  /// 打开应用所需的Box
  static Future<void> _openBoxes() async {
    // 示例:加密Box
    // final encryptionKey = Hive.generateSecureKey();
    // await Hive.openBox<NoteModel>('notes', encryptionCipher: HiveAesCipher(encryptionKey));
    
    await Hive.openBox<NoteModel>('notes');
    await Hive.openBox('settings');
  }

  /// 获取Box实例
  static Box<T> getBox<T>(String boxName) {
    if (!Hive.isBoxOpen(boxName)) {
      throw StateError('Box "$boxName"未打开');
    }
    return Hive.box<T>(boxName);
  }

  /// 关闭所有Box
  static Future<void> dispose() async {
    await Hive.close();
  }
}
3. 仓储层实现

dart

复制代码
// lib/features/notes/data/repositories/note_repository_impl.dart
import 'package:dartz/dartz.dart';
import 'package:hive/hive.dart';
import 'package:equatable/equatable.dart';

import '../../../../core/error/failures.dart';
import '../../domain/entities/note.dart';
import '../../domain/repositories/note_repository.dart';
import '../models/note_model.dart';
import '../../../../core/storage/hive_service.dart';

class NoteRepositoryImpl implements NoteRepository {
  final Box<NoteModel> _noteBox;

  NoteRepositoryImpl() : _noteBox = HiveService.getBox<NoteModel>('notes');

  @override
  Future<Either<Failure, Note>> addNote(Note note) async {
    try {
      final noteModel = NoteModel(
        id: note.id,
        title: note.title,
        content: note.content,
        createdAt: note.createdAt,
        updatedAt: note.updatedAt,
        tags: note.tags,
      );
      
      await _noteBox.put(note.id, noteModel);
      return Right(note);
    } catch (e) {
      return Left(CacheFailure(message: e.toString()));
    }
  }

  @override
  Future<Either<Failure, List<Note>>> getAllNotes() async {
    try {
      final notes = _noteBox.values
          .map((model) => Note(
                id: model.id,
                title: model.title,
                content: model.content,
                createdAt: model.createdAt,
                updatedAt: model.updatedAt,
                tags: model.tags,
              ))
          .toList();
      
      return Right(notes);
    } catch (e) {
      return Left(CacheFailure(message: e.toString()));
    }
  }

  @override
  Future<Either<Failure, Note?>> getNoteById(String id) async {
    try {
      final model = _noteBox.get(id);
      if (model == null) {
        return const Right(null);
      }
      
      final note = Note(
        id: model.id,
        title: model.title,
        content: model.content,
        createdAt: model.createdAt,
        updatedAt: model.updatedAt,
        tags: model.tags,
      );
      
      return Right(note);
    } catch (e) {
      return Left(CacheFailure(message: e.toString()));
    }
  }

  // 实现其他方法...
}
三、SQLite:结构化数据存储方案

对于需要复杂查询和关系管理的数据,SQLite 是最佳选择:

dart

复制代码
// lib/core/database/app_database.dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:flutter/foundation.dart';

/// SQLite数据库管理类
class AppDatabase {
  static Database? _database;
  static const String _dbName = 'app_database.db';
  static const int _version = 2; // 当前数据库版本

  /// 获取数据库单例
  static Future<Database> get instance async {
    if (_database != null) return _database!;
    
    _database = await _initDatabase();
    return _database!;
  }

  /// 初始化数据库
  static Future<Database> _initDatabase() async {
    final databasesPath = await getDatabasesPath();
    final path = join(databasesPath, _dbName);

    return await openDatabase(
      path,
      version: _version,
      onCreate: _onCreate,
      onUpgrade: _onUpgrade,
      onDowngrade: _onDowngrade,
    );
  }

  /// 数据库创建时调用
  static Future<void> _onCreate(Database db, int version) async {
    // 创建用户表
    await db.execute('''
      CREATE TABLE users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        email TEXT UNIQUE NOT NULL,
        name TEXT NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      )
    ''');

    // 创建订单表
    await db.execute('''
      CREATE TABLE orders (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER NOT NULL,
        total_amount REAL NOT NULL,
        status TEXT NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
      )
    ''');
  }

  /// 数据库升级时调用
  static Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
    debugPrint('升级数据库: $oldVersion -> $newVersion');
    
    if (oldVersion < 2) {
      // 版本1升级到版本2:添加用户头像字段
      await db.execute('ALTER TABLE users ADD COLUMN avatar_url TEXT');
    }
  }

  /// 数据库降级时调用
  static Future<void> _onDowngrade(Database db, int oldVersion, int newVersion) async {
    // 处理降级逻辑...
  }

  /// 关闭数据库
  static Future<void> close() async {
    if (_database != null) {
      await _database!.close();
      _database = null;
    }
  }

  /// 执行事务
  static Future<T> transaction<T>(Future<T> Function(Database) action) async {
    final db = await instance;
    return await db.transaction(action);
  }
}

数据访问层示例

dart

复制代码
// lib/features/users/data/datasources/user_local_data_source.dart
import 'package:sqflite/sqflite.dart';
import '../../../../core/database/app_database.dart';
import '../models/user_model.dart';

abstract class UserLocalDataSource {
  Future<UserModel> getUserById(int id);
  Future<int> insertUser(UserModel user);
  Future<List<UserModel>> getAllUsers();
}

class UserLocalDataSourceImpl implements UserLocalDataSource {
  @override
  Future<UserModel> getUserById(int id) async {
    final db = await AppDatabase.instance;
    final maps = await db.query(
      'users',
      where: 'id = ?',
      whereArgs: [id],
      limit: 1,
    );

    if (maps.isNotEmpty) {
      return UserModel.fromMap(maps.first);
    } else {
      throw Exception('User not found');
    }
  }

  @override
  Future<int> insertUser(UserModel user) async {
    return await AppDatabase.transaction((db) async {
      return await db.insert(
        'users',
        user.toMap(),
        conflictAlgorithm: ConflictAlgorithm.replace,
      );
    });
  }

  // 其他方法实现...
}
四、存储方案选型指南
特性 SharedPreferences Hive SQLite
数据类型 简单键值对 对象、集合、自定义类型 结构化关系数据
查询能力 仅按 key 查询 基本查询、索引支持 SQL 查询、JOIN、事务
性能 快(小数据量) 极快(所有数据内存中) 稳定(适合大数据量)
数据大小 适合 < 1MB 适合 < 100MB 无限制
加密支持 无(需自行加密) 内置加密 需自行实现
学习曲线
适用场景 配置、Token、简单偏好设置 缓存、复杂对象、本地列表 用户数据、订单、关系型数据
五、企业级存储最佳实践
  1. 分层架构设计

    • 数据层:原始存储实现(SharedPreferences/Hive/SQLite)
    • 仓储层:封装数据操作逻辑,提供清晰接口
    • 领域层:定义业务实体和规则,与存储实现解耦
  2. 错误处理策略

    • 使用 Either 模式区分成功 / 失败结果
    • 统一的异常处理和日志记录
    • 适当的重试机制和回滚策略
  3. 性能优化技巧

    • 批量操作使用事务
    • 大数据集使用分页加载
    • 索引优化(SQLite)
    • 内存缓存热点数据
  4. 安全考量

    • 敏感数据加密存储
    • 避免存储明文密码
    • 使用安全的密钥管理方案
  5. 测试策略

    • 抽象存储接口便于 Mock 测试
    • 单元测试覆盖核心存储逻辑
    • 集成测试验证端到端数据流程

总结

Flutter 本地存储的核心要点:

  1. 选型匹配场景:根据数据复杂度和访问模式选择合适的存储方案,简单配置用 SharedPreferences,复杂对象用 Hive,结构化数据用 SQLite。

  2. 架构分层设计:通过仓储模式封装存储实现,隔离业务逻辑与数据访问,提高代码可维护性和可测试性。

  3. 关注性能与安全:合理使用事务、索引和缓存优化性能,对敏感数据实施加密保护,遵循数据安全最佳实践。

通过遵循这些最佳实践,你可以构建出健壮、高效且易于维护的 Flutter 本地存储系统,满足从简单到复杂的应用需求。

相关推荐
tsyjjOvO18 小时前
SpringMVC 从入门到精通
数据仓库·hive·hadoop
白藏y18 小时前
【数据库】SQLite的基础使用
数据库·sqlite
程序员Ctrl喵1 天前
异步编程:Event Loop 与 Isolate 的深层博弈
开发语言·flutter
前端不太难1 天前
Flutter 如何设计可长期维护的模块边界?
flutter
小蜜蜂嗡嗡1 天前
flutter列表中实现置顶动画
flutter
始持1 天前
第十二讲 风格与主题统一
前端·flutter
始持1 天前
第十一讲 界面导航与路由管理
flutter·vibecoding
始持1 天前
第十三讲 异步操作与异步构建
前端·flutter
新镜1 天前
【Flutter】 视频视频源横向、竖向问题
flutter
黄林晴1 天前
Compose Multiplatform 1.10 发布:统一 Preview、Navigation 3、Hot Reload 三箭齐发
android·flutter