当前位置: 首页 > news >正文

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提供了多种本地存储方案,选择合适的方案取决于你的具体需求:

  1. SharedPreferences:适合简单的键值对存储,如用户偏好设置
  2. Hive:适合对象存储,高性能且类型安全
  3. SQFlite:适合复杂的关系型数据存储,支持SQL查询
  4. File:适合文件存储,如图片、缓存文件

核心要点:

  • 根据数据类型和大小选择合适的存储方案
  • 使用异步操作避免阻塞UI线程
  • 对敏感数据进行加密处理
  • 考虑数据迁移和版本管理

掌握这些存储方案后,你可以根据项目需求灵活选择合适的本地存储策略。

http://www.jsqmd.com/news/848266/

相关文章:

  • 专业的有机颜料厂家
  • 无王无帝定乾坤,来自田间第一人 凰标立定新格局
  • BombLab通关后,我总结了这7个Linux调试与逆向的实战技巧
  • Perplexity × 音乐版权合规性审计:1份自动生成DMCA豁免声明的Prompt模板,已通过3家律所验证
  • 2026年高评价眉毛培训优质机构推荐:零基础学纹眉、零基础小白、零基础纹眉学校、零结痂雾眉、韩式定妆学校、韩式眉学校选择指南 - 优质品牌商家
  • 绕过SuppressIldasm保护?聊聊.NET程序集反编译的那些事儿与安全边界
  • 如何用嘎嘎降AI处理医学论文:临床医学毕业论文降AI免费完整操作教程
  • 毫米波雷达舱内检测避坑指南:从TI Demo到量产,如何搞定B柱安装与复杂环境干扰?
  • 【Linux安装Docker】
  • 大连天车/龙门吊/航车/航吊/行吊/起重机销售/安装/维修/维保/威拓重机、鸿岳起重|全品类起重机一站式服务
  • 无王无帝定乾坤,来自田间第一人 第一大道渡凡尘
  • 保姆级教程:在Ubuntu 20.04上搞定Intel RealSense D435i与ROS Noetic的联调(含RK3588避坑指南)
  • 2026年圆形冷却塔品牌技术解析:常州良机冷却塔、无锡冷却塔维修、无锡良机冷却塔、昆山冷却塔维修、昆山良机冷却塔选择指南 - 优质品牌商家
  • 【c++面向对象编程】第32篇:移动语义与右值引用:现代C++性能优化核心
  • 渗透测试中的Windows痕迹清理:从“删库跑路”到“雁过无痕”的反取证艺术
  • 如何选择适合数据中心的电源设备:技术路线与品牌决策的全面分析
  • PyTorch实战:手把手教你用GAN生成‘以假乱真’的MNIST数字,并打包成新Dataset
  • d2s-editor:重新定义暗黑破坏神2存档编辑工作流的现代化解决方案
  • 从Assimp的Scene对象到你的屏幕:一个3D模型在OpenGL中的完整‘旅程’(附C++代码拆解)
  • 2026年至今,谁在引领湖北船撞防护系统技术革新?深度解析武汉中创的行业领导力 - 2026年企业推荐榜
  • Betaflight 4.5硬件配置文件深度解析:如何为你的飞控板添加对新传感器(如ICM42688P)的支持
  • 打卡信奥刷题(3286)用C++实现信奥题 P8929 「TERRA-OI R1」别得意,小子
  • 2025最权威的十大AI写作方案横评
  • 如何通过3个简单步骤实现网盘文件直链下载:LinkSwift浏览器脚本完全指南
  • RePKG终极指南:Wallpaper Engine资源高效提取与转换实战
  • 3分钟快速上手LyricsX:打造专属桌面歌词体验的完整指南
  • 2026年绝缘臂高空作业车售后保障深度评测报告:绝缘曲臂高空作业车/绝缘直臂高空作业车/绝缘臂高空作业车/带电高空作业车/选择指南 - 优质品牌商家
  • War3地图制作入门:不用写代码,用触发器和变量也能做出有趣玩法
  • 别再只用ARIMA了!用PyTorch Forecasting的TFT搞定多变量时序预测(含完整代码)
  • 告别轮询!在RuoYi-Vue-Plus 3.5.0中集成WebSocket实现消息实时推送(附Undertow适配踩坑记录)