Flutter主流的本地存储方案

224 阅读5分钟

我来为您详细介绍Flutter主流的本地存储方案,包括使用场景、区别和实际代码示例。 主要包括 SharedPreferences、Hive、SQLite、path_provider、ObjectBox等

1. Shared Preferences

使用场景

  • 简单的键值对存储
  • 用户偏好设置(主题、语言等)
  • 登录状态、用户配置
  • 小型数据缓存

特点

  • 基于平台原生存储(iOS的NSUserDefaults,Android的SharedPreferences)
  • 只支持基本数据类型(String, int, double, bool, List)
  • 性能中等,适合少量数据

实际代码示例

import 'package:shared_preferences/shared_preferences.dart';

class SharedPreferencesService {
  static const String _themeKey = 'theme';
  static const String _languageKey = 'language';
  static const String _userTokenKey = 'user_token';
  static const String _firstLaunchKey = 'first_launch';
  
  // 保存主题设置
  static Future<void> saveTheme(String theme) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(_themeKey, theme);
  }
  
  // 获取主题设置
  static Future<String?> getTheme() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString(_themeKey);
  }
  
  // 保存用户token
  static Future<void> saveUserToken(String token) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(_userTokenKey, token);
  }
  
  // 获取用户token
  static Future<String?> getUserToken() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString(_userTokenKey);
  }
  
  // 标记首次启动
  static Future<void> markFirstLaunch() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool(_firstLaunchKey, false);
  }
  
  // 检查是否首次启动
  static Future<bool> isFirstLaunch() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getBool(_firstLaunchKey) ?? true;
  }
  
  // 清除所有数据
  static Future<void> clearAll() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.clear();
  }
}

2. Hive

使用场景

  • 复杂对象存储
  • 大量数据存储
  • 需要高性能读写的场景
  • 离线数据缓存

特点

  • 纯Dart实现,跨平台一致性好
  • 支持自定义对象存储
  • 高性能(比SQLite更快)
  • 支持加密

实际代码示例

import 'package:hive/hive.dart';

part 'user_model.g.dart';

@HiveType(typeId: 0)
class UserModel extends HiveObject {
  @HiveField(0)
  final String id;
  
  @HiveField(1)
  final String name;
  
  @HiveField(2)
  final String email;
  
  @HiveField(3)
  final DateTime createdAt;
  
  @HiveField(4)
  final Map<String, dynamic> preferences;
  
  UserModel({
    required this.id,
    required this.name,
    required this.email,
    required this.createdAt,
    required this.preferences,
  });
}

part 'product_model.g.dart';

@HiveType(typeId: 1)
class ProductModel extends HiveObject {
  @HiveField(0)
  final String id;
  
  @HiveField(1)
  final String name;
  
  @HiveField(2)
  final double price;
  
  @HiveField(3)
  final int stock;
  
  @HiveField(4)
  final List<String> categories;
  
  ProductModel({
    required this.id,
    required this.name,
    required this.price,
    required this.stock,
    required this.categories,
  });
}

class HiveService {
  static const String _userBox = 'users';
  static const String _productBox = 'products';
  static const String _settingsBox = 'settings';
  
  static Future<void> init() async {
    await Hive.initFlutter();
    
    // 注册适配器
    Hive.registerAdapter(UserModelAdapter());
    Hive.registerAdapter(ProductModelAdapter());
    
    // 打开盒子
    await Hive.openBox<UserModel>(_userBox);
    await Hive.openBox<ProductModel>(_productBox);
    await Hive.openBox(_settingsBox); // 普通盒子,存储基本类型
  }
  
  // 用户相关操作
  static Future<void> saveUser(UserModel user) async {
    final box = Hive.box<UserModel>(_userBox);
    await box.put(user.id, user);
  }
  
  static UserModel? getUser(String userId) {
    final box = Hive.box<UserModel>(_userBox);
    return box.get(userId);
  }
  
  static List<UserModel> getAllUsers() {
    final box = Hive.box<UserModel>(_userBox);
    return box.values.toList();
  }
  
  // 产品相关操作
  static Future<void> saveProduct(ProductModel product) async {
    final box = Hive.box<ProductModel>(_productBox);
    await box.put(product.id, product);
  }
  
  static List<ProductModel> getProductsByCategory(String category) {
    final box = Hive.box<ProductModel>(_productBox);
    return box.values
        .where((product) => product.categories.contains(category))
        .toList();
  }
  
  // 批量操作
  static Future<void> saveProducts(List<ProductModel> products) async {
    final box = Hive.box<ProductModel>(_productBox);
    final Map<String, ProductModel> data = {};
    for (var product in products) {
      data[product.id] = product;
    }
    await box.putAll(data);
  }
  
  // 使用事务
  static Future<void> updateProductStock(String productId, int newStock) async {
    final box = Hive.box<ProductModel>(_productBox);
    await Hive.runInTransaction(() async {
      final product = box.get(productId);
      if (product != null) {
        final updatedProduct = ProductModel(
          id: product.id,
          name: product.name,
          price: product.price,
          stock: newStock,
          categories: product.categories,
        );
        await box.put(productId, updatedProduct);
      }
    });
  }
}

3. SQLite (sqflite)

使用场景

  • 复杂的关系型数据
  • 需要SQL查询的场景
  • 数据统计分析
  • 大量结构化数据

特点

  • 完整的SQL支持
  • 支持事务
  • 成熟稳定
  • 性能优秀

实际代码示例

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class OrderModel {
  final int? id;
  final String orderId;
  final String userId;
  final double totalAmount;
  final DateTime orderDate;
  final String status;
  final List<OrderItem> items;
  
  OrderModel({
    this.id,
    required this.orderId,
    required this.userId,
    required this.totalAmount,
    required this.orderDate,
    required this.status,
    required this.items,
  });
  
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'order_id': orderId,
      'user_id': userId,
      'total_amount': totalAmount,
      'order_date': orderDate.millisecondsSinceEpoch,
      'status': status,
    };
  }
}

class OrderItem {
  final int? id;
  final String orderId;
  final String productId;
  final int quantity;
  final double unitPrice;
  
  OrderItem({
    this.id,
    required this.orderId,
    required this.productId,
    required this.quantity,
    required this.unitPrice,
  });
  
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'order_id': orderId,
      'product_id': productId,
      'quantity': quantity,
      'unit_price': unitPrice,
    };
  }
}

class DatabaseService {
  static Database? _database;
  static const int _version = 1;
  
  static Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }
  
  static Future<Database> _initDatabase() async {
    final path = join(await getDatabasesPath(), 'ecommerce.db');
    return openDatabase(
      path,
      version: _version,
      onCreate: (db, version) async {
        // 创建订单表
        await db.execute('''
          CREATE TABLE orders(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            order_id TEXT UNIQUE NOT NULL,
            user_id TEXT NOT NULL,
            total_amount REAL NOT NULL,
            order_date INTEGER NOT NULL,
            status TEXT NOT NULL
          )
        ''');
        
        // 创建订单项表
        await db.execute('''
          CREATE TABLE order_items(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            order_id TEXT NOT NULL,
            product_id TEXT NOT NULL,
            quantity INTEGER NOT NULL,
            unit_price REAL NOT NULL,
            FOREIGN KEY (order_id) REFERENCES orders (order_id)
          )
        ''');
        
        // 创建索引
        await db.execute('CREATE INDEX idx_orders_user_id ON orders(user_id)');
        await db.execute('CREATE INDEX idx_orders_date ON orders(order_date)');
      },
      onUpgrade: (db, oldVersion, newVersion) async {
        // 数据库升级逻辑
        if (oldVersion < 2) {
          // 添加新字段或表
        }
      },
    );
  }
  
  // 插入订单(使用事务)
  static Future<int> insertOrder(OrderModel order) async {
    final db = await database;
    
    return await db.transaction((txn) async {
      // 插入订单
      final orderId = await txn.insert(
        'orders',
        order.toMap(),
        conflictAlgorithm: ConflictAlgorithm.replace,
      );
      
      // 插入订单项
      for (var item in order.items) {
        await txn.insert(
          'order_items',
          item.toMap(),
          conflictAlgorithm: ConflictAlgorithm.replace,
        );
      }
      
      return orderId;
    });
  }
  
  // 查询用户订单(带分页)
  static Future<List<OrderModel>> getUserOrders(
    String userId, {
    int page = 1,
    int pageSize = 20,
  }) async {
    final db = await database;
    final offset = (page - 1) * pageSize;
    
    final List<Map<String, dynamic>> orderMaps = await db.query(
      'orders',
      where: 'user_id = ?',
      whereArgs: [userId],
      orderBy: 'order_date DESC',
      limit: pageSize,
      offset: offset,
    );
    
    final orders = <OrderModel>[];
    
    for (var orderMap in orderMaps) {
      // 查询订单项
      final List<Map<String, dynamic>> itemMaps = await db.query(
        'order_items',
        where: 'order_id = ?',
        whereArgs: [orderMap['order_id']],
      );
      
      final items = itemMaps.map((itemMap) => OrderItem(
        id: itemMap['id'],
        orderId: itemMap['order_id'],
        productId: itemMap['product_id'],
        quantity: itemMap['quantity'],
        unitPrice: itemMap['unit_price'],
      )).toList();
      
      orders.add(OrderModel(
        id: orderMap['id'],
        orderId: orderMap['order_id'],
        userId: orderMap['user_id'],
        totalAmount: orderMap['total_amount'],
        orderDate: DateTime.fromMillisecondsSinceEpoch(orderMap['order_date']),
        status: orderMap['status'],
        items: items,
      ));
    }
    
    return orders;
  }
  
  // 复杂查询:统计用户消费
  static Future<Map<String, dynamic>> getUserOrderStats(String userId) async {
    final db = await database;
    
    final result = await db.rawQuery('''
      SELECT 
        COUNT(*) as total_orders,
        SUM(total_amount) as total_spent,
        AVG(total_amount) as avg_order_value,
        MAX(total_amount) as max_order_value
      FROM orders 
      WHERE user_id = ? AND status = 'completed'
    ''', [userId]);
    
    if (result.isNotEmpty) {
      return {
        'total_orders': result.first['total_orders'] ?? 0,
        'total_spent': result.first['total_spent'] ?? 0.0,
        'avg_order_value': result.first['avg_order_value'] ?? 0.0,
        'max_order_value': result.first['max_order_value'] ?? 0.0,
      };
    }
    
    return {
      'total_orders': 0,
      'total_spent': 0.0,
      'avg_order_value': 0.0,
      'max_order_value': 0.0,
    };
  }
}

4. 文件存储 (path_provider)

使用场景

  • 大文件存储(图片、文档等)
  • 自定义文件格式
  • 需要直接文件操作的场景
  • 应用日志记录

特点

  • 灵活性强
  • 适合大文件
  • 需要手动管理文件路径

实际代码示例

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

class FileStorageService {
  // 获取不同类型的目录
  static Future<Directory> get documentsDirectory async {
    return await getApplicationDocumentsDirectory();
  }
  
  static Future<Directory> get tempDirectory async {
    return await getTemporaryDirectory();
  }
  
  static Future<Directory> get externalStorageDirectory async {
    return await getExternalStorageDirectory() ?? await documentsDirectory;
  }
  
  // 保存JSON配置文件
  static Future<void> saveConfig(Map<String, dynamic> config) async {
    final dir = await documentsDirectory;
    final file = File('${dir.path}/app_config.json');
    await file.writeAsString(json.encode(config));
  }
  
  // 读取JSON配置文件
  static Future<Map<String, dynamic>> loadConfig() async {
    try {
      final dir = await documentsDirectory;
      final file = File('${dir.path}/app_config.json');
      if (await file.exists()) {
        final contents = await file.readAsString();
        return json.decode(contents);
      }
    } catch (e) {
      print('Error loading config: $e');
    }
    return {};
  }
  
  // 保存图片文件
  static Future<String> saveImage(File imageFile, String fileName) async {
    final dir = await documentsDirectory;
    final imagesDir = Directory('${dir.path}/images');
    if (!await imagesDir.exists()) {
      await imagesDir.create(recursive: true);
    }
    
    final newPath = '${imagesDir.path}/$fileName';
    await imageFile.copy(newPath);
    return newPath;
  }
  
  // 保存应用日志
  static Future<void> log(String message) async {
    try {
      final dir = await documentsDirectory;
      final logFile = File('${dir.path}/app.log');
      final timestamp = DateTime.now().toIso8601String();
      final logEntry = '$timestamp: $message\n';
      
      // 追加写入
      final sink = logFile.openWrite(mode: FileMode.append);
      sink.write(logEntry);
      await sink.close();
      
      // 控制日志文件大小(最大10MB)
      await _rotateLogFile(logFile);
    } catch (e) {
      print('Error writing log: $e');
    }
  }
  
  static Future<void> _rotateLogFile(File logFile) async {
    const maxSize = 10 * 1024 * 1024; // 10MB
    if (await logFile.exists()) {
      final length = await logFile.length();
      if (length > maxSize) {
        // 读取最后1000行
        final lines = await logFile.readAsLines();
        final recentLines = lines.length > 1000 ? lines.sublist(lines.length - 1000) : lines;
        await logFile.writeAsString(recentLines.join('\n') + '\n');
      }
    }
  }
  
  // 备份数据到文件
  static Future<void> backupData(Map<String, dynamic> data) async {
    final dir = await documentsDirectory;
    final backupDir = Directory('${dir.path}/backups');
    if (!await backupDir.exists()) {
      await backupDir.create(recursive: true);
    }
    
    final timestamp = DateTime.now().millisecondsSinceEpoch;
    final backupFile = File('${backupDir.path}/backup_$timestamp.json');
    await backupFile.writeAsString(json.encode(data));
    
    // 清理旧的备份文件(保留最近5个)
    await _cleanOldBackups(backupDir);
  }
  
  static Future<void> _cleanOldBackups(Directory backupDir) async {
    final files = await backupDir.list().toList();
    final backupFiles = files.whereType<File>().toList();
    
    if (backupFiles.length > 5) {
      // 按修改时间排序,删除最旧的
      backupFiles.sort((a, b) => a.statSync().modified.compareTo(b.statSync().modified));
      for (int i = 0; i < backupFiles.length - 5; i++) {
        await backupFiles[i].delete();
      }
    }
  }
}

5. ObjectBox

使用场景

  • 超高性能需求
  • 大量对象存储
  • 实时数据同步
  • 复杂对象关系

特点

  • 性能极高
  • 支持实时查询
  • 自动关系管理
  • 学习曲线较陡

实际代码示例

import 'package:objectbox/objectbox.dart';

@Entity()
class Customer {
  int id = 0;
  String name;
  String email;
  
  @Property(unique: true)
  final String customerId;
  
  final ToMany<Order> orders = ToMany<Order>();
  
  Customer({
    this.id = 0,
    required this.name,
    required this.email,
    required this.customerId,
  });
}

@Entity()
class Order {
  int id = 0;
  double totalAmount;
  DateTime orderDate;
  String status;
  
  final ToOne<Customer> customer = ToOne<Customer>();
  final ToMany<Product> products = ToMany<Product>();
  
  Order({
    this.id = 0,
    required this.totalAmount,
    required this.orderDate,
    required this.status,
  });
}

class ObjectBoxService {
  late Store store;
  late Box<Customer> customerBox;
  late Box<Order> orderBox;
  
  Future<void> init() async {
    store = await openStore();
    customerBox = store.box<Customer>();
    orderBox = store.box<Order>();
  }
  
  // 保存客户
  int saveCustomer(Customer customer) {
    return customerBox.put(customer);
  }
  
  // 查询客户(支持复杂条件)
  List<Customer> findCustomersByName(String name) {
    final query = customerBox.query(
      Customer_.name.contains(name, caseSensitive: false)
    ).build();
    return query.find();
  }
  
  // 实时查询(监听数据变化)
  Stream<List<Customer>> watchCustomers() {
    final query = customerBox.query().build();
    return query.watch(triggerImmediately: true).map((query) => query.find());
  }
}

使用区别对比

特性SharedPreferencesHiveSQLite文件存储ObjectBox
数据类型基本类型自定义对象结构化数据任意数据复杂对象
性能中等中等极高
查询能力有限完整SQL实时查询
事务支持
加密支持需手动
学习曲线简单中等中等简单较陡
适用场景配置项对象存储关系数据大文件高性能

综合使用示例

class StorageManager {
  static Future<void> init() async {
    // 初始化所有存储方案
    await HiveService.init();
    await DatabaseService.database; // 初始化SQLite
    await ObjectBoxService().init(); // 如果有ObjectBox
  }
  
  // 根据数据类型选择合适的存储方案
  static dynamic saveData(String type, dynamic data) {
    switch (type) {
      case 'user_preferences':
        return SharedPreferencesService.saveUserPreferences(data);
      case 'user_object':
        return HiveService.saveUser(data);
      case 'order_data':
        return DatabaseService.insertOrder(data);
      case 'large_file':
        return FileStorageService.saveFile(data);
      case 'high_perf_object':
        return ObjectBoxService().saveObject(data);
      default:
        throw Exception('Unsupported data type: $type');
    }
  }
}

选择建议

  1. 简单配置:SharedPreferences
  2. 对象存储:Hive(平衡性能和学习成本)
  3. 关系数据:SQLite(需要复杂查询时)
  4. 大文件:文件存储
  5. 极致性能:ObjectBox

根据您的具体需求选择合适的存储方案,或者组合使用以获得最佳效果。