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线程
  • 对敏感数据进行加密处理
  • 考虑数据迁移和版本管理

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

更多推荐