Flutter本地存储完全指南
·
Flutter本地存储完全指南
引言
在移动应用开发中,本地存储是必不可少的功能,用于持久化保存用户数据、配置信息、缓存内容等。Flutter提供了多种本地存储方案,每种方案适用于不同的场景。本文将详细介绍Flutter中常用的本地存储方式及其最佳实践。
本地存储方案对比
| 方案 | 适用场景 | 数据类型 | 数据大小 | 优点 | 缺点 |
|---|---|---|---|---|---|
| SharedPreferences | 简单键值对 | 基本类型 | 小 | 简单易用 | 仅支持基本类型 |
| Hive | 对象存储 | 任意对象 | 中 | 高性能、类型安全 | 需要定义Adapter |
| SQFlite | 关系型数据库 | 结构化数据 | 大 | 支持复杂查询 | 学习成本高 |
| File | 文件存储 | 任意数据 | 大 | 灵活 | 需要手动管理 |
SharedPreferences
安装与配置
dependencies:
shared_preferences: ^2.2.2
基本用法
import 'package:shared_preferences/shared_preferences.dart';
class PreferencesService {
Future<void> saveString(String key, String value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(key, value);
}
Future<String?> getString(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
}
Future<void> saveInt(String key, int value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(key, value);
}
Future<int?> getInt(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getInt(key);
}
Future<void> saveBool(String key, bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(key, value);
}
Future<bool?> getBool(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(key);
}
Future<void> remove(String key) async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(key);
}
Future<void> clear() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
}
}
实际应用
// 保存用户偏好设置
final service = PreferencesService();
await service.saveString('username', 'john_doe');
await service.saveBool('dark_mode', true);
await service.saveInt('notifications_count', 5);
// 读取用户偏好设置
final username = await service.getString('username');
final darkMode = await service.getBool('dark_mode') ?? false;
final count = await service.getInt('notifications_count') ?? 0;
Hive
安装与配置
dependencies:
hive: ^2.2.3
hive_flutter: ^1.1.0
dev_dependencies:
hive_generator: ^1.1.5
build_runner: ^2.4.4
创建数据模型
import 'package:hive/hive.dart';
part 'user.g.dart';
@HiveType(typeId: 0)
class User {
@HiveField(0)
final String id;
@HiveField(1)
final String name;
@HiveField(2)
final String email;
@HiveField(3)
final int age;
@HiveField(4)
final DateTime createdAt;
User({
required this.id,
required this.name,
required this.email,
required this.age,
required this.createdAt,
});
}
生成Adapter
运行以下命令生成Adapter:
flutter packages pub run build_runner build
初始化Hive
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'user.dart';
void main() async {
await Hive.initFlutter();
Hive.registerAdapter(UserAdapter());
await Hive.openBox<User>('users');
runApp(const MyApp());
}
基本操作
class HiveService {
final Box<User> _userBox;
HiveService(this._userBox);
// 添加用户
Future<void> addUser(User user) async {
await _userBox.put(user.id, user);
}
// 获取用户
User? getUser(String id) {
return _userBox.get(id);
}
// 获取所有用户
List<User> getAllUsers() {
return _userBox.values.toList();
}
// 更新用户
Future<void> updateUser(User user) async {
await _userBox.put(user.id, user);
}
// 删除用户
Future<void> deleteUser(String id) async {
await _userBox.delete(id);
}
// 查询用户
List<User> searchUsers(String query) {
return _userBox.values
.where((user) =>
user.name.toLowerCase().contains(query.toLowerCase()) ||
user.email.toLowerCase().contains(query.toLowerCase())
)
.toList();
}
}
使用示例
// 创建HiveService
final userBox = Hive.box<User>('users');
final service = HiveService(userBox);
// 添加用户
final user = User(
id: '1',
name: 'John Doe',
email: 'john@example.com',
age: 30,
createdAt: DateTime.now(),
);
await service.addUser(user);
// 获取用户
final retrievedUser = service.getUser('1');
print(retrievedUser?.name); // John Doe
// 查询用户
final results = service.searchUsers('john');
print(results.length); // 1
SQFlite
安装与配置
dependencies:
sqflite: ^2.3.2
path_provider: ^2.1.1
创建数据库
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
final documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, 'app_database.db');
return await openDatabase(
path,
version: 1,
onCreate: _onCreate,
onUpgrade: _onUpgrade,
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE users (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
age INTEGER,
created_at TEXT NOT NULL
)
''');
await db.execute('''
CREATE TABLE posts (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT,
created_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
)
''');
}
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
// 数据库升级逻辑
if (oldVersion < 2) {
// 添加新字段或表
}
}
}
CRUD操作
class UserDao {
final Database _db;
UserDao(this._db);
Future<void> insertUser(User user) async {
await _db.insert(
'users',
{
'id': user.id,
'name': user.name,
'email': user.email,
'age': user.age,
'created_at': user.createdAt.toIso8601String(),
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<User?> getUser(String id) async {
final maps = await _db.query(
'users',
where: 'id = ?',
whereArgs: [id],
);
if (maps.isNotEmpty) {
return User.fromMap(maps.first);
}
return null;
}
Future<List<User>> getAllUsers() async {
final maps = await _db.query('users');
return maps.map((map) => User.fromMap(map)).toList();
}
Future<int> updateUser(User user) async {
return await _db.update(
'users',
{
'name': user.name,
'email': user.email,
'age': user.age,
},
where: 'id = ?',
whereArgs: [user.id],
);
}
Future<int> deleteUser(String id) async {
return await _db.delete(
'users',
where: 'id = ?',
whereArgs: [id],
);
}
Future<List<User>> getUsersByAge(int minAge, int maxAge) async {
final maps = await _db.query(
'users',
where: 'age BETWEEN ? AND ?',
whereArgs: [minAge, maxAge],
orderBy: 'age ASC',
);
return maps.map((map) => User.fromMap(map)).toList();
}
}
User模型
class User {
final String id;
final String name;
final String email;
final int? age;
final DateTime createdAt;
User({
required this.id,
required this.name,
required this.email,
this.age,
required this.createdAt,
});
factory User.fromMap(Map<String, dynamic> map) {
return User(
id: map['id'] as String,
name: map['name'] as String,
email: map['email'] as String,
age: map['age'] as int?,
createdAt: DateTime.parse(map['created_at'] as String),
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'email': email,
'age': age,
'created_at': createdAt.toIso8601String(),
};
}
}
使用示例
// 获取数据库实例
final dbHelper = DatabaseHelper();
final db = await dbHelper.database;
final userDao = UserDao(db);
// 插入用户
final user = User(
id: '1',
name: 'John Doe',
email: 'john@example.com',
age: 30,
createdAt: DateTime.now(),
);
await userDao.insertUser(user);
// 查询用户
final retrievedUser = await userDao.getUser('1');
print(retrievedUser?.name); // John Doe
// 查询年龄在20-35岁之间的用户
final users = await userDao.getUsersByAge(20, 35);
print(users.length);
File存储
基本文件操作
import 'dart:io';
import 'package:path_provider/path_provider.dart';
class FileStorageService {
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> _localFile(String filename) async {
final path = await _localPath;
return File('$path/$filename');
}
Future<void> writeString(String filename, String content) async {
final file = await _localFile(filename);
await file.writeAsString(content);
}
Future<String> readString(String filename) async {
try {
final file = await _localFile(filename);
return await file.readAsString();
} catch (e) {
return '';
}
}
Future<void> writeBytes(String filename, List<int> bytes) async {
final file = await _localFile(filename);
await file.writeAsBytes(bytes);
}
Future<List<int>> readBytes(String filename) async {
try {
final file = await _localFile(filename);
return await file.readAsBytes();
} catch (e) {
return [];
}
}
Future<bool> fileExists(String filename) async {
final file = await _localFile(filename);
return await file.exists();
}
Future<void> deleteFile(String filename) async {
final file = await _localFile(filename);
if (await file.exists()) {
await file.delete();
}
}
}
保存JSON数据
import 'dart:convert';
final storage = FileStorageService();
// 保存JSON数据
final user = {
'id': '1',
'name': 'John Doe',
'email': 'john@example.com',
};
final jsonString = json.encode(user);
await storage.writeString('user.json', jsonString);
// 读取JSON数据
final jsonData = await storage.readString('user.json');
final userMap = json.decode(jsonData) as Map<String, dynamic>;
print(userMap['name']); // John Doe
选择合适的存储方案
选择指南
数据量小 数据量大
↓ ↓
简单键值对 SharedPreferences SQFlite
对象存储 Hive SQFlite
文件存储 File File
推荐使用场景
| 场景 | 推荐方案 |
|---|---|
| 用户设置/偏好 | SharedPreferences |
| 小型对象存储 | Hive |
| 复杂查询/关系数据 | SQFlite |
| 文件/图片/缓存 | File |
性能优化
批量操作
// SQFlite批量插入
Future<void> insertUsers(List<User> users) async {
final db = await database;
final batch = db.batch();
for (var user in users) {
batch.insert('users', user.toMap());
}
await batch.commit(noResult: true);
}
异步操作
// 避免阻塞UI线程
Future<void> loadData() async {
// 使用isolate或后台线程处理大量数据
final data = await compute(_parseData, rawData);
setState(() {
_items = data;
});
}
List<Item> _parseData(String rawData) {
// 耗时的数据解析
return rawData.split(',').map((e) => Item(e)).toList();
}
缓存策略
class CacheService {
final Duration _expiryDuration = const Duration(hours: 1);
Future<void> cacheData(String key, dynamic data) async {
final cache = {
'data': data,
'timestamp': DateTime.now().toIso8601String(),
};
await _preferences.setString(key, json.encode(cache));
}
Future<dynamic?> getCachedData(String key) async {
final cacheString = await _preferences.getString(key);
if (cacheString == null) return null;
final cache = json.decode(cacheString) as Map<String, dynamic>;
final timestamp = DateTime.parse(cache['timestamp'] as String);
if (DateTime.now().difference(timestamp) > _expiryDuration) {
await _preferences.remove(key);
return null;
}
return cache['data'];
}
}
数据迁移
数据库版本迁移
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
if (oldVersion == 1 && newVersion == 2) {
// 添加新字段
await db.execute('ALTER TABLE users ADD COLUMN phone TEXT');
}
if (oldVersion == 2 && newVersion == 3) {
// 创建新表
await db.execute('''
CREATE TABLE settings (
id TEXT PRIMARY KEY,
key TEXT UNIQUE,
value TEXT
)
''');
}
}
安全注意事项
1. 敏感数据加密
import 'package:encrypt/encrypt.dart';
class SecureStorage {
final String _key = 'your-encryption-key';
String encrypt(String plainText) {
final key = Key.fromUtf8(_key.padRight(32));
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key));
return encrypter.encrypt(plainText, iv: iv).base64;
}
String decrypt(String encryptedText) {
final key = Key.fromUtf8(_key.padRight(32));
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key));
return encrypter.decrypt64(encryptedText, iv: iv);
}
}
2. 避免存储敏感信息
// 错误做法:存储密码
await prefs.setString('password', 'secret123');
// 正确做法:存储token或使用Keychain/Keystore
await prefs.setString('auth_token', 'jwt-token-here');
总结
Flutter提供了多种本地存储方案,选择合适的方案取决于你的具体需求:
- SharedPreferences:适合简单的键值对存储,如用户偏好设置
- Hive:适合对象存储,高性能且类型安全
- SQFlite:适合复杂的关系型数据存储,支持SQL查询
- File:适合文件存储,如图片、缓存文件
核心要点:
- 根据数据类型和大小选择合适的存储方案
- 使用异步操作避免阻塞UI线程
- 对敏感数据进行加密处理
- 考虑数据迁移和版本管理
掌握这些存储方案后,你可以根据项目需求灵活选择合适的本地存储策略。
更多推荐
所有评论(0)