Flutter 本地存储方案:SharedPreferences、SQFlite 与 Hive

Flutter 本地存储方案:SharedPreferences、SQFlite 与 Hive

在 Flutter 应用开发中,本地存储是实现数据持久化的核心需求,广泛应用于保存用户配置、缓存网络数据、存储离线信息等场景。当前 Flutter 生态中,SharedPreferences、SQFlite、Hive 是最主流的三种本地存储方案,它们分别基于不同的存储原理,适配不同的数据规模与业务场景。本文将从核心原理、实战实现、优缺点分析、横向对比及选型建议五个维度,全面解析这三种方案,帮助开发者快速选择适配自身项目的本地存储方案。

作者:爱吃大芒果

个人主页 爱吃大芒果

本文所属专栏 Flutter

更多专栏

Ascend C 算子开发教程(进阶)
鸿蒙集成
从0到1自学C++

一、核心原理:三种方案的存储本质差异

本地存储的核心是将数据持久化到设备本地,三种方案的本质差异在于存储载体与数据结构,直接决定了其适用场景与性能表现。

1. SharedPreferences:键值对轻量存储

SharedPreferences 是基于平台原生键值对存储的封装:在 Android 端封装了 SharedPreferences,在 iOS 端封装了 NSUserDefaults。其核心特点是"轻量、简单",数据以键值对(Key-Value)形式存储,支持字符串、整数、布尔值、浮点数等基础数据类型,但不支持复杂对象直接存储。底层存储载体为 XML 文件(Android)或 plist 文件(iOS),适合存储少量简单数据。

2. SQFlite:本地关系型数据库

SQFlite 是 SQLite 数据库在 Flutter 中的封装,SQLite 是一款嵌入式关系型数据库,支持标准 SQL 语法。其核心特点是"结构化、支持复杂查询",数据存储在独立的数据库文件中,通过表结构定义数据格式,支持多表关联、事务、索引等关系型数据库特性。适合存储大量结构化数据,如用户列表、订单记录等。

3. Hive:NoSQL 文档型数据库

Hive 是专为 Flutter 设计的 NoSQL 文档型数据库,基于键值对存储,但支持复杂对象直接序列化存储。其核心特点是"高性能、跨平台、无依赖",底层采用二进制格式存储数据,无需额外的原生依赖(纯 Dart 实现),支持自定义对象存储(通过 TypeAdapter 实现序列化),同时提供了类似 SQL 的查询能力(如过滤、排序)。兼顾了 SharedPreferences 的简单性与 SQFlite 的数据管理能力,适合中大量复杂对象存储。

二、实战实现:同一需求的三种存储方式

以"存储用户信息(用户名、年龄、是否登录)"为实战需求,分别展示三种方案的实现流程与核心代码,直观感受其使用差异。

1. SharedPreferences 实现:轻量键值对存储

SharedPreferences 实现需经历"添加依赖→初始化→存储数据→读取数据→删除数据"五个步骤,核心代码如下:

(1)添加依赖
yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.2  # 最新稳定版
(2)核心实现代码
dart 复制代码
import 'package:shared_preferences/shared_preferences.dart';

// 封装 SharedPreferences 工具类
class SpUtil {
  // 初始化 SharedPreferences(需在使用前调用)
  static late SharedPreferences _prefs;
  static Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
  }

  // 存储用户信息(基础数据类型)
  static Future<void> saveUserInfo({
    required String username,
    required int age,
    required bool isLogin,
  }) async {
    await _prefs.setString('username', username);
    await _prefs.setInt('age', age);
    await _prefs.setBool('isLogin', isLogin);
  }

  // 读取用户信息
  static Map<String, dynamic> getUserInfo() {
    return {
      'username': _prefs.getString('username') ?? '',
      'age': _prefs.getInt('age') ?? 0,
      'isLogin': _prefs.getBool('isLogin') ?? false,
    };
  }

  // 删除单个用户信息
  static Future<void> removeUserInfo(String key) async {
    await _prefs.remove(key);
  }

  // 清空所有存储数据
  static Future<void> clearAll() async {
    await _prefs.clear();
  }
}

// 调用示例
void main() async {
  // 必须先初始化
  await SpUtil.init();

  // 存储用户信息
  await SpUtil.saveUserInfo(
    username: 'FlutterDeveloper',
    age: 25,
    isLogin: true,
  );

  // 读取用户信息
  final userInfo = SpUtil.getUserInfo();
  print('用户名:${userInfo['username']},年龄:${userInfo['age']},登录状态:${userInfo['isLogin']}');

  // 删除单个数据
  await SpUtil.removeUserInfo('age');

  // 清空所有数据
  // await SpUtil.clearAll();
}

核心特点:API 简洁直观,无需关注底层实现,适合存储少量基础类型数据。但不支持复杂对象直接存储,需手动将对象转换为基础类型(如 JSON 字符串)。

2. SQFlite 实现:结构化数据库存储

SQFlite 实现需经历"添加依赖→创建数据库与表→CRUD 操作→关闭数据库"五个步骤,核心代码如下:

(1)添加依赖
yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.3.0  # 核心依赖
  path: ^1.8.3      # 用于获取数据库文件路径
(2)核心实现代码
dart 复制代码
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

// 定义用户模型
class User {
  final String username;
  final int age;
  final bool isLogin;

  User({
    required this.username,
    required this.age,
    required this.isLogin,
  });

  // 转换为 Map(用于存储)
  Map<String, dynamic> toMap() {
    return {
      'username': username,
      'age': age,
      'isLogin': isLogin ? 1 : 0, // SQLite 不支持布尔值,用 0/1 替代
    };
  }

  // 从 Map 转换为 User(用于读取)
  static User fromMap(Map<String, dynamic> map) {
    return User(
      username: map['username'],
      age: map['age'],
      isLogin: map['isLogin'] == 1,
    );
  }
}

// 封装 SQFlite 工具类
class DbUtil {
  static late Database _database;
  static const String dbName = 'user_db.db'; // 数据库名
  static const String tableName = 'user';    // 表名

  // 初始化数据库
  static Future<void> init() async {
    // 获取数据库存储路径
    String dbPath = await getDatabasesPath();
    String path = join(dbPath, dbName);

    // 打开数据库(不存在则创建)
    _database = await openDatabase(
      path,
      version: 1, // 数据库版本
      onCreate: (db, version) {
        // 创建表结构
        db.execute('''
          CREATE TABLE $tableName (
            username TEXT PRIMARY KEY,
            age INTEGER,
            isLogin INTEGER
          )
        ''');
      },
    );
  }

  // 插入用户信息(新增/更新)
  static Future<void> insertUser(User user) async {
    await _database.insert(
      tableName,
      user.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace, // 存在则替换
    );
  }

  // 查询所有用户信息
  static Future<List<User>> queryAllUsers() async {
    final List<Map<String, dynamic>> maps = await _database.query(tableName);
    return List.generate(maps.length, (i) => User.fromMap(maps[i]));
  }

  // 根据用户名查询用户
  static Future<User?> queryUserByUsername(String username) async {
    final List<Map<String, dynamic>> maps = await _database.query(
      tableName,
      where: 'username = ?',
      whereArgs: [username], // 防止 SQL 注入
    );
    return maps.isNotEmpty ? User.fromMap(maps.first) : null;
  }

  // 删除用户
  static Future<void> deleteUser(String username) async {
    await _database.delete(
      tableName,
      where: 'username = ?',
      whereArgs: [username],
    );
  }

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

// 调用示例
void main() async {
  // 初始化数据库
  await DbUtil.init();

  // 插入用户信息
  final user = User(
    username: 'FlutterDeveloper',
    age: 25,
    isLogin: true,
  );
  await DbUtil.insertUser(user);

  // 查询所有用户
  final allUsers = await DbUtil.queryAllUsers();
  print('所有用户:${allUsers.map((u) => u.username).toList()}');

  // 根据用户名查询
  final queryUser = await DbUtil.queryUserByUsername('FlutterDeveloper');
  if (queryUser != null) {
    print('查询用户:${queryUser.username},年龄:${queryUser.age}');
  }

  // 删除用户
  await DbUtil.deleteUser('FlutterDeveloper');

  // 关闭数据库(一般在应用退出时调用)
  // await DbUtil.closeDb();
}

核心特点:支持结构化数据存储与复杂查询,适合大量数据管理。但需手动设计表结构,处理数据类型转换(如布尔值),SQL 语法有一定学习成本。

3. Hive 实现:高性能复杂对象存储

Hive 实现需经历"添加依赖→初始化→注册适配器→存储数据→读取数据"五个步骤,核心代码如下:

(1)添加依赖
yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  hive: ^2.2.3          # 核心依赖
  hive_flutter: ^1.1.0  # Flutter 适配依赖(提供路径管理等)

dev_dependencies:
  hive_generator: ^1.1.5  # 代码生成工具
  build_runner: ^2.4.4    # 代码生成工具
(2)核心实现代码
dart 复制代码
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';

// 1. 定义用户模型(添加 @HiveType 注解)
part 'user.g.dart'; // 生成的适配器文件

@HiveType(typeId: 0) // typeId 为唯一标识(0-223)
class User {
  @HiveField(0) // 字段标识(唯一)
  final String username;

  @HiveField(1)
  final int age;

  @HiveField(2)
  final bool isLogin;

  User({
    required this.username,
    required this.age,
    required this.isLogin,
  });
}

// 2. 生成适配器(执行命令:flutter pub run build_runner build)
// 生成后会自动创建 user.g.dart 文件

// 3. 封装 Hive 工具类
class HiveUtil {
  static late Box<User> _userBox;
  static const String boxName = 'user_box'; // 盒子名(Hive 中数据存储在盒子里)

  // 初始化 Hive
  static Future<void> init() async {
    await Hive.initFlutter(); // 初始化 Flutter 适配
    Hive.registerAdapter(UserAdapter()); // 注册适配器(必须)
    _userBox = await Hive.openBox<User>(boxName); // 打开盒子
  }

  // 存储用户信息(key 为用户名,value 为 User 对象)
  static void putUser(String key, User user) {
    _userBox.put(key, user);
  }

  // 根据 key 读取用户信息
  static User? getUser(String key) {
    return _userBox.get(key);
  }

  // 读取所有用户信息
  static Map<String, User> getAllUsers() {
    return _userBox.toMap().cast<String, User>();
  }

  // 删除用户信息
  static void deleteUser(String key) {
    _userBox.delete(key);
  }

  // 清空盒子
  static Future<void> clearBox() async {
    await _userBox.clear();
  }

  // 关闭 Hive
  static Future<void> closeHive() async {
    await Hive.close();
  }
}

// 调用示例
void main() async {
  // 初始化 Hive
  await HiveUtil.init();

  // 存储用户信息
  final user = User(
    username: 'FlutterDeveloper',
    age: 25,
    isLogin: true,
  );
  HiveUtil.putUser('FlutterDeveloper', user);

  // 读取用户信息
  final queryUser = HiveUtil.getUser('FlutterDeveloper');
  if (queryUser != null) {
    print('用户名:${queryUser.username},年龄:${queryUser.age},登录状态:${queryUser.isLogin}');
  }

  // 读取所有用户
  final allUsers = HiveUtil.getAllUsers();
  print('所有用户:${allUsers.keys.toList()}');

  // 删除用户
  HiveUtil.deleteUser('FlutterDeveloper');

  // 清空盒子
  // await HiveUtil.clearBox();

  // 关闭 Hive
  // await HiveUtil.closeHive();
}

核心特点:支持复杂对象直接存储(无需手动转换),API 简洁,性能优异,纯 Dart 实现无原生依赖。需通过代码生成工具创建适配器,适配复杂对象存储。

三、优缺点深度分析:适配场景决定选型

结合实战体验,从数据类型、性能、易用性、扩展性等维度,深度剖析三种方案的优缺点,明确其适用边界。

1. SharedPreferences 优缺点

优点 :① 易用性极高,API 简洁直观,开发成本低;② 轻量级,无额外依赖,集成简单;③ 跨平台适配良好,底层基于原生实现,稳定性高;④ 适合存储少量配置信息(如登录状态、主题设置)。
缺点:① 仅支持基础数据类型,不支持复杂对象直接存储;② 性能较差,大量数据存储时读取速度慢;③ 无事务支持,数据一致性难以保证;④ 不支持复杂查询,仅能通过键获取值。

2. SQFlite 优缺点

优点 :① 支持结构化数据存储,表结构清晰,适合大量数据管理;② 支持标准 SQL 语法,可实现复杂查询(多条件过滤、排序、关联查询等);③ 支持事务,保证数据一致性;④ 稳定性高,适配所有 Flutter 支持的平台。
缺点:① 易用性较差,需手动设计表结构、处理数据类型转换;② SQL 语法有一定学习成本,开发效率低;③ 复杂对象存储需手动序列化/反序列化;④ 性能一般,大量数据读写时效率低于 Hive。

3. Hive 优缺点

优点 :① 性能优异,底层二进制存储,读写速度快于 SQFlite;② 支持复杂对象直接存储,通过适配器实现自动化序列化;③ 纯 Dart 实现,无原生依赖,跨平台适配更简单;④ API 简洁直观,兼顾易用性与扩展性;⑤ 支持事务、索引、查询过滤等高级特性。
缺点:① 复杂对象存储需通过代码生成工具创建适配器,额外增加配置步骤;② 生态相对 SQFlite 较新,部分边缘场景支持不足;③ 不支持 SQL 语法,复杂查询需通过 Hive 自带 API 实现,灵活性低于 SQFlite。

四、横向对比:一张表看懂核心差异

对比维度 SharedPreferences SQFlite Hive
存储类型 键值对(基础类型) 关系型数据库(结构化) NoSQL 文档型(键值对+复杂对象)
支持数据类型 字符串、整数、布尔值、浮点数、列表 文本、整数、浮点数、 blob 等(需手动转换布尔值) 所有 Dart 基础类型+自定义对象
易用性 ★★★★★(极高) ★★☆(较低) ★★★★(较高)
性能(大量数据) ★☆(较差) ★★★(一般) ★★★★★(极佳)
复杂查询支持 ★★★★★(SQL 语法,灵活) ★★★(自带 API,有限灵活)
事务支持
跨平台依赖 依赖原生实现 依赖原生 SQLite 纯 Dart,无依赖
适用数据规模 少量(KB 级) 大量(MB/GB 级) 中大量(MB 级)
扩展能力 强(SQL 扩展) 强(自定义适配器)

五、实战选型建议:匹配项目与业务需求

本地存储方案的选型需结合数据规模、数据类型、查询需求、开发效率四大核心因素,以下是针对性建议:

1. 优先选 SharedPreferences 的场景

① 存储少量配置信息(如登录状态、主题模式、语言设置、用户 Token);② 数据类型简单(仅基础类型),无需复杂查询;③ 追求极致开发效率,无需额外配置;④ 小型工具类 App 或 MVP 原型验证。

2. 优先选 SQFlite 的场景

① 存储大量结构化数据(如用户列表、订单记录、离线缓存的商品数据);② 需要复杂查询(如多条件过滤、排序、分组、多表关联);③ 对数据一致性要求高,需要事务支持;④ 团队熟悉 SQL 语法,可接受较高的开发成本。

3. 优先选 Hive 的场景

① 存储中大量复杂对象(如自定义模型、嵌套数据结构);② 追求高性能读写,对响应速度要求高;③ 跨平台适配需求严格(如需要适配 Web 端,避免原生依赖);④ 兼顾易用性与扩展性,既不想写 SQL,又需要比 SharedPreferences 更强的功能。

4. 混合使用建议

大型项目可采用"主方案+辅助方案"的混合模式:① 用 SharedPreferences 存储配置信息(如登录状态、Token);② 用 SQFlite 或 Hive 存储核心业务数据(如用户信息、订单列表)。例如:电商 App 中,用 SharedPreferences 保存用户登录状态,用 Hive 存储离线商品缓存,用 SQFlite 存储复杂的订单历史数据。

六、注意事项与最佳实践

无论选择哪种存储方案,都需注意以下核心要点,提升数据存储的稳定性与安全性:

  • 数据加密:敏感数据(如用户密码、Token)需加密后存储,可使用 encrypt 库实现 AES 加密;

  • 初始化时机:所有存储方案的初始化都需在异步中完成,建议在 main 函数中初始化,确保使用前已完成;

  • 资源释放:SQFlite 与 Hive 需在应用退出时关闭,避免资源泄漏;

  • 数据迁移:SQFlite 需妥善处理数据库版本升级与表结构迁移,Hive 需处理适配器版本兼容;

  • 性能优化:① SharedPreferences 避免频繁读写大量数据;② SQFlite 合理创建索引提升查询速度;③ Hive 避免在主线程进行大量数据读写,可使用 compute 进行异步处理。

七、结语

SharedPreferences、SQFlite、Hive 三种本地存储方案无绝对优劣,核心差异在于适配场景:SharedPreferences 胜在"简单、轻量",适合少量配置;SQFlite 胜在"结构化、强查询",适合大量复杂数据;Hive 胜在"高性能、跨平台、支持复杂对象",兼顾易用性与扩展性。

开发者在选型时,应跳出"技术优劣"的误区,聚焦业务需求:明确数据规模、数据类型、查询复杂度,结合团队技术栈选择最能降低开发成本、提升应用性能的方案。同时,合理运用混合存储模式,可最大化发挥各方案的优势,构建稳定、高效的本地存储体系。

相关推荐
淼淼7634 小时前
Qt工具栏+图页,图元支持粘贴复制,撤销,剪切,移动,删除
开发语言·c++·windows·qt
Kelvin_Ngan4 小时前
Qt包含QtCharts/QValueAxis时编译报错
开发语言·qt
葱卤山猪4 小时前
【Qt】心跳检测与粘包处理:打造稳定可靠的TCP Socket通信
开发语言·数据库·qt
a程序小傲4 小时前
淘宝Java面试被问:Atomic原子类的实现原理
java·开发语言·后端·面试
laocooon5238578864 小时前
C++中的安全指针(智能指针)
开发语言·c++
咸鱼加辣4 小时前
【python面试题】LRUCache
开发语言·python
LitchiCheng4 小时前
WSL2 中 pynput 无法捕获按键输入?
开发语言·python
中年程序员一枚4 小时前
Python 中处理视频添加 / 替换音频
开发语言·python·音视频
yuuki2332334 小时前
【C++】模板初阶
java·开发语言·c++