Flutter 本地存储完全指南

引言

本地存储是移动应用开发中不可或缺的一部分,它允许应用在设备上持久化数据。Flutter 提供了多种本地存储方案,本文将深入探讨各种存储方式和最佳实践。

存储方案概述

方案 适用场景 数据类型 大小限制
SharedPreferences 简单键值对 基本类型 较小
GetStorage 简单键值对 基本类型 较大
SQLite 复杂数据 关系型数据 较大
Hive NoSQL 数据库 对象 较大
File 文件存储 任意文件 较大

高级技巧一:SharedPreferences

基本用法

import 'package:shared_preferences/shared_preferences.dart';

// 保存数据
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('counter', 42);
await prefs.setString('username', 'John');
await prefs.setBool('isDarkMode', true);

// 读取数据
int? counter = prefs.getInt('counter');
String? username = prefs.getString('username');
bool? isDarkMode = prefs.getBool('isDarkMode');

// 删除数据
await prefs.remove('counter');
await prefs.clear();

封装存储服务

class PreferencesService {
  static late SharedPreferences _prefs;
  
  static Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
  }
  
  static int get counter => _prefs.getInt('counter') ?? 0;
  static set counter(int value) => _prefs.setInt('counter', value);
  
  static String get username => _prefs.getString('username') ?? '';
  static set username(String value) => _prefs.setString('username', value);
  
  static bool get isDarkMode => _prefs.getBool('isDarkMode') ?? false;
  static set isDarkMode(bool value) => _prefs.setBool('isDarkMode', value);
}

高级技巧二:GetStorage

基本用法

import 'package:get_storage/get_storage.dart';

// 初始化
await GetStorage.init();

final box = GetStorage();

// 保存数据
box.write('user', {'name': 'John', 'age': 30});
box.write('counter', 42);

// 读取数据
Map<String, dynamic>? user = box.read('user');
int counter = box.read('counter') ?? 0;

// 删除数据
box.remove('user');
box.erase();

监听变化

box.listenKey('counter', (value) {
  print('Counter changed: $value');
});

高级技巧三:Hive 数据库

配置 Hive

import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';

// 初始化
await Hive.initFlutter();

// 注册适配器
Hive.registerAdapter(UserAdapter());

// 打开盒子
final box = await Hive.openBox<User>('users');

创建数据模型

part 'user.g.dart';

@HiveType(typeId: 0)
class User {
  @HiveField(0)
  final String name;
  
  @HiveField(1)
  final int age;
  
  @HiveField(2)
  final String email;
  
  User({required this.name, required this.age, required this.email});
}

操作数据

// 添加数据
final user = User(name: 'John', age: 30, email: 'john@example.com');
await box.add(user);

// 获取数据
User? user = box.getAt(0);
User? userByKey = box.get('user1');

// 更新数据
user.name = 'John Updated';
await user.save();

// 删除数据
await box.deleteAt(0);
await box.clear();

高级技巧四:SQLite 数据库

使用 moor

import 'package:moor/moor.dart';
import 'package:moor_flutter/moor_flutter.dart';

// 创建数据库
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(FlutterQueryExecutor.inDatabaseFolder(path: 'db.sqlite'));
  
  @override
  int get schemaVersion => 1;
  
  // 表定义
  final users = Table('users');
  final id = users.int().autoIncrement();
  final name = users.text();
  final age = users.integer();
}

// 操作数据
final db = AppDatabase();

// 插入
await db.into(db.users).insert(UsersCompanion.insert(
  name: 'John',
  age: 30,
));

// 查询
final users = await db.select(db.users).get();
final user = await (db.select(db.users)..where((u) => u.id.equals(1))).getSingle();

// 更新
await (db.update(db.users)..where((u) => u.id.equals(1))).write(UsersCompanion(
  name: Value('John Updated'),
));

// 删除
await (db.delete(db.users)..where((u) => u.id.equals(1))).go();

高级技巧五:文件存储

基本文件操作

import 'dart:io';
import 'package:path_provider/path_provider.dart';

// 获取目录
final directory = await getApplicationDocumentsDirectory();
final filePath = '${directory.path}/data.txt';

// 写入文件
await File(filePath).writeAsString('Hello, World!');

// 读取文件
String content = await File(filePath).readAsString();

// 删除文件
await File(filePath).delete();

序列化对象

import 'dart:convert';

// 序列化
final user = {'name': 'John', 'age': 30};
final jsonString = jsonEncode(user);
await File(filePath).writeAsString(jsonString);

// 反序列化
final jsonString = await File(filePath).readAsString();
final user = jsonDecode(jsonString) as Map<String, dynamic>;

实战案例:主题状态持久化

class ThemeService {
  static const String _key = 'theme_mode';
  
  static Future<ThemeMode> getThemeMode() async {
    final prefs = await SharedPreferences.getInstance();
    final mode = prefs.getString(_key);
    
    switch (mode) {
      case 'dark':
        return ThemeMode.dark;
      case 'light':
        return ThemeMode.light;
      default:
        return ThemeMode.system;
    }
  }
  
  static Future<void> setThemeMode(ThemeMode mode) async {
    final prefs = await SharedPreferences.getInstance();
    String value;
    
    switch (mode) {
      case ThemeMode.dark:
        value = 'dark';
        break;
      case ThemeMode.light:
        value = 'light';
        break;
      default:
        value = 'system';
    }
    
    await prefs.setString(_key, value);
  }
}

实战案例:用户数据持久化

class UserRepository {
  final Box<User> _box;
  
  UserRepository(this._box);
  
  Future<void> saveUser(User user) async {
    await _box.put(user.id.toString(), user);
  }
  
  User? getUser(String id) {
    return _box.get(id);
  }
  
  Future<void> deleteUser(String id) async {
    await _box.delete(id);
  }
  
  List<User> getAllUsers() {
    return _box.values.toList();
  }
}

常见问题与解决方案

Q1:如何处理数据迁移?

A:使用 Hive 的迁移机制:

final box = await Hive.openBox('data', migration: (oldVersion, newVersion) {
  if (oldVersion == 1) {
    // 迁移逻辑
  }
});

Q2:如何加密数据?

A:使用加密包:

import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:encrypt/encrypt.dart';

// 创建加密适配器
final encryptionKey = Hive.generateSecureKey();
final box = await Hive.openBox('secureBox', encryptionCipher: HiveAesCipher(encryptionKey));

Q3:如何处理大文件?

A:使用文件分块读写:

final file = File('large_file.bin');
final stream = file.openRead();
await for (var chunk in stream) {
  // 处理每个块
}

最佳实践

1. 封装存储逻辑

// 错误:直接在 Widget 中使用
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('counter', 42);

// 正确:封装到服务类
await PreferencesService.setCounter(42);

2. 使用异步操作

// 错误:同步阻塞
final value = prefs.getInt('key');

// 正确:异步处理
final value = await PreferencesService.getValue();

3. 处理异常

try {
  await db.insert(user);
} catch (e) {
  // 处理错误
}

总结

本地存储是 Flutter 应用的重要组成部分。通过本文的学习,你应该能够:

  1. 选择合适的存储方案
  2. 使用 SharedPreferences 存储简单数据
  3. 使用 Hive 存储复杂对象
  4. 使用 SQLite 存储关系型数据
  5. 实现数据持久化和恢复

掌握这些技巧,能够帮助你构建更加可靠的应用。

更多推荐