在 Flutter 应用开发中,本地存储是必不可少的功能。无论是保存用户登录信息、应用配置,还是存储结构化的业务数据,都需要选择合适的本地存储方案。Flutter 提供了多种本地存储方式,涵盖轻量级键值对存储、NoSQL 数据库和关系型数据库等,满足不同场景的需求。本文将详细解析 Flutter 中主流的本地存储方案,包括其特点、用法和适用场景。
一、Flutter 本地存储的常见场景与分类
Flutter 本地存储主要用于以下场景:
- 保存轻量级配置信息(如主题模式、语言设置);
- 缓存网络数据,减少重复请求;
- 存储结构化业务数据(如本地订单、收藏列表);
- 持久化用户登录状态、个人信息等。
根据存储数据的类型和结构,Flutter 本地存储可分为三类:
- 键值对存储 :适合存储简单的键值型数据,如
SharedPreferences、Hive(轻量 NoSQL,也支持键值对); - NoSQL 数据库 :适合存储非结构化或半结构化数据,如
Hive、ObjectBox; - 关系型数据库 :适合存储结构化、关联度高的数据,如
SQFlite、Drift。
二、SharedPreferences:轻量级键值对存储
SharedPreferences是 Flutter 中最常用的轻量级存储方案,本质上是对 Android 的SharedPreferences和 iOS 的NSUserDefaults的封装,适用于存储简单的键值对数据(如字符串、数字、布尔值)。
1. 集成与基本使用
首先在pubspec.yaml中添加依赖:
dependencies:
shared_preferences: ^2.2.2
核心操作包括存、取、删、清:
Dart
import 'package:shared_preferences/shared_preferences.dart';
// 存储数据
Future<void> saveData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('username', 'FlutterDev'); // 存储字符串
await prefs.setInt('age', 25); // 存储整数
await prefs.setBool('isLogin', true); // 存储布尔值
await prefs.setStringList('hobbies', ['coding', 'reading']); // 存储字符串列表
}
// 获取数据
Future<void> getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? username = prefs.getString('username');
int? age = prefs.getInt('age');
bool? isLogin = prefs.getBool('isLogin');
List<String>? hobbies = prefs.getStringList('hobbies');
print("用户名:$username,年龄:$age,是否登录:$isLogin,爱好:$hobbies");
}
// 删除数据
Future<void> removeData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove('username'); // 删除指定键的数据
}
// 清空所有数据
Future<void> clearData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.clear();
}
2. 适用场景与局限性
适用场景 :存储应用配置、用户偏好设置、简单的状态标记(如是否首次启动)。局限性:
- 仅支持基本数据类型,无法存储复杂对象;
- 存储容量有限,不适合存储大量数据;
- 性能一般,频繁读写可能导致卡顿。
三、Hive:高性能 NoSQL 键值数据库
Hive是专为 Flutter 设计的轻量级 NoSQL 数据库,基于键值对存储,支持复杂对象序列化,性能远超SharedPreferences,且无需原生依赖,跨平台兼容性好。
1. 集成与初始化
添加依赖并配置:
Dart
dependencies:
hive: ^2.2.3
hive_flutter: ^1.1.0
dev_dependencies:
hive_generator: ^1.1.5
build_runner: ^2.4.6
初始化 Hive:
Dart
import 'package:hive_flutter/hive_flutter.dart';
void initHive() async {
await Hive.initFlutter(); // 初始化Hive
await Hive.openBox('userBox'); // 打开名为userBox的盒子(类似数据库表)
}
2. 基本操作
Hive 的核心操作围绕 Box(盒子) 展开,一个 Box 相当于一个数据集:
Dart
// 存储数据
void saveToHive() {
final box = Hive.box('userBox');
box.put('user', {'name': 'FlutterDev', 'age': 25}); // 存储Map
box.put('scores', [90, 85, 95]); // 存储列表
}
// 获取数据
void getFromHive() {
final box = Hive.box('userBox');
var user = box.get('user');
var scores = box.get('scores');
print("用户信息:$user,分数:$scores");
}
// 自定义对象存储(需序列化)
// 1. 创建实体类并添加注解
import 'package:hive/hive.dart';
part 'person.g.dart'; // 生成的序列化代码
@HiveType(typeId: 0)
class Person {
@HiveField(0)
final String name;
@HiveField(1)
final int age;
Person({required this.name, required this.age});
}
// 2. 运行构建命令生成序列化代码:flutter pub run build_runner build
// 3. 注册适配器并存储对象
void saveCustomObject() async {
Hive.registerAdapter(PersonAdapter()); // 注册适配器
final box = Hive.box('userBox');
box.put('person', Person(name: 'FlutterDev', age: 25));
Person? person = box.get('person');
print("自定义对象:${person?.name},${person?.age}");
}
3. 适用场景与优势
适用场景 :存储复杂对象、中等规模的本地数据、需要高性能读写的场景(如缓存列表数据)。优势:
- 支持复杂对象序列化,无需手动转换;
- 纯 Dart 实现,跨平台无原生依赖;
- 读写速度快,性能优于 SQFlite;
- 支持加密存储,保护敏感数据。
四、SQFlite:SQLite 的 Flutter 封装
SQFlite是 Flutter 对 SQLite 数据库的封装,适用于存储结构化、关联度高的关系型数据,如电商订单、本地通讯录等。
1. 集成与数据库初始化
添加依赖:
bash
dependencies:
sqflite: ^2.3.0
path: ^1.8.3
初始化数据库并创建表:
Dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static final DatabaseHelper instance = DatabaseHelper._init();
static Database? _database;
DatabaseHelper._init();
// 获取数据库实例
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB('notes.db');
return _database!;
}
// 初始化数据库
Future<Database> _initDB(String filePath) async {
final dbPath = await getDatabasesPath();
final path = join(dbPath, filePath);
return await openDatabase(path, version: 1, onCreate: _createDB);
}
// 创建表
Future<void> _createDB(Database db, int version) async {
await db.execute('''
CREATE TABLE notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
)
''');
}
}
2. CRUD 操作
Dart
// 插入数据
Future<int> insertNote(Map<String, dynamic> note) async {
final db = await DatabaseHelper.instance.database;
return await db.insert('notes', note);
}
// 查询所有数据
Future<List<Map<String, dynamic>>> queryAllNotes() async {
final db = await DatabaseHelper.instance.database;
return await db.query('notes');
}
// 更新数据
Future<int> updateNote(Map<String, dynamic> note) async {
final db = await DatabaseHelper.instance.database;
int id = note['id'];
return await db.update('notes', note, where: 'id = ?', whereArgs: [id]);
}
// 删除数据
Future<int> deleteNote(int id) async {
final db = await DatabaseHelper.instance.database;
return await db.delete('notes', where: 'id = ?', whereArgs: [id]);
}
3. 适用场景与局限性
适用场景 :存储结构化、有复杂关联关系的数据,如多表关联的业务数据。局限性:
- 需要编写 SQL 语句,学习成本较高;
- 性能略低于 Hive,适合数据量中等的场景;
- 跨平台需适配不同系统的 SQLite 版本。
五、Drift:类型安全的 SQL 数据库
Drift(原 Moor)是基于 SQLite 的高级封装,采用 Dart 代码生成的方式实现类型安全的数据库操作,无需手动编写 SQL 语句,开发效率更高。
1. 集成与基本使用
添加依赖:
Dart
dependencies:
drift: ^2.12.0
sqlite3_flutter_libs: ^0.5.16
path_provider: ^2.1.1
path: ^1.8.3
dev_dependencies:
drift_dev: ^2.12.0
build_runner: ^2.4.6
定义数据库表和操作:
Dart
import 'package:drift/drift.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:drift/native.dart';
import 'dart:io';
// 定义表
class Notes extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().notNull()();
TextColumn get content => text().nullable()();
DateTimeColumn get createTime => dateTime().withDefault(currentDateAndTime)();
}
// 定义数据库类
part 'database.g.dart';
@DriftDatabase(tables: [Notes])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
// 插入笔记
Future<int> insertNote(NotesCompanion note) => into(notes).insert(note);
// 查询所有笔记
Future<List<Note>> getAllNotes() => select(notes).get();
// 更新笔记
Future<int> updateNote(Note note) => update(notes).replace(note);
// 删除笔记
Future<int> deleteNote(Note note) => delete(notes).delete(note);
}
// 打开数据库连接
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'notes.db'));
return NativeDatabase(file);
});
}
运行构建命令生成代码:flutter pub run build_runner build,之后即可调用数据库方法:
Dart
void testDrift() async {
final db = AppDatabase();
// 插入数据
await db.insertNote(NotesCompanion(title: Value('Flutter笔记'), content: Value('Drift数据库学习')));
// 查询数据
List<Note> notes = await db.getAllNotes();
print(notes);
}
2. 优势与适用场景
优势:
- 类型安全,编译期检查 SQL 错误;
- 支持 Dart 语法代替 SQL 语句,开发更便捷;
- 支持流查询(
watch方法),数据变化时自动刷新 UI; - 兼容 SQLite 所有特性,支持复杂查询和事务。
适用场景:需要类型安全、复杂查询的关系型数据存储场景,适合中大型 Flutter 应用。
六、Flutter 本地存储方案的选择建议
- 简单键值对数据 :优先选择
SharedPreferences,开发成本低;若需要存储复杂对象或追求高性能,选择Hive; - 非结构化 / 半结构化数据 :选择
Hive,兼顾性能和易用性; - 结构化、关联度高的数据 :
- 若熟悉 SQL 且数据量较小,选择
SQFlite; - 若追求类型安全和开发效率,选择
Drift;
- 若熟悉 SQL 且数据量较小,选择
- 大数据量、高性能要求 :考虑
ObjectBox(另一个高性能 NoSQL 数据库,适合海量数据存储)。
七、本地存储的注意事项
- 数据加密 :敏感数据(如用户密码、token)需加密存储,可使用
encrypt包结合 Hive/SQFlite 实现; - 数据清理:定期清理过期缓存数据,避免占用过多设备存储;
- 事务管理:批量操作数据时使用事务,确保数据一致性;
- 错误处理:捕获存储操作中的异常(如数据库打开失败、读写权限不足),避免程序崩溃;
- 适配不同平台:注意 Android 和 iOS 的存储路径、权限差异,确保跨平台兼容性。
八、总结
Flutter 提供了丰富的本地存储方案,从轻量级的SharedPreferences到高性能的Hive,再到关系型的SQFlite和Drift,每种方案都有其适用场景。在实际开发中,需根据数据类型、规模和性能需求选择合适的存储方式,并遵循最佳实践确保数据安全和应用稳定性。掌握这些本地存储技术,能让 Flutter 应用在离线场景下也能提供流畅的用户体验。