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 本地存储系统,满足从简单到复杂的应用需求。

相关推荐
i***11869 小时前
Django视图与URLs路由详解
数据库·django·sqlite
San30.10 小时前
AIGC 时代如何优雅地操作数据库:SQLite + Python 实战与 SQL Prompt Engineering
数据库·sqlite·aigc
Qin_jiangshan20 小时前
flutter和reactNative以及uniapp区别
flutter·react native·uni-app
旧时光_21 小时前
第5章:容器类组件 —— 5.1 填充(Padding)
flutter
renxhui21 小时前
Flutter 基础控件速查(面向 Android 开发者)
flutter
A懿轩A21 小时前
【2025版 OpenHarmony】 GitCode 口袋工具:Flutter + Dio 网路请求 打造随身的鸿蒙版 GitCode 搜索助手
windows·flutter·华为·鸿蒙·openharmony·开源鸿蒙
QuantumLeap丶21 小时前
《Flutter全栈开发实战指南:从零到高级》- 20 -主题与国际化
flutter·ios·前端框架
心随雨下21 小时前
Flutter动画系统详解
flutter