Flutter主流的本地存储方案

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

1. Shared Preferences

使用场景

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

特点

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

实际代码示例

dart 复制代码
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更快)
  • 支持加密

实际代码示例

dart 复制代码
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支持
  • 支持事务
  • 成熟稳定
  • 性能优秀

实际代码示例

dart 复制代码
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)

使用场景

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

特点

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

实际代码示例

dart 复制代码
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

使用场景

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

特点

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

实际代码示例

dart 复制代码
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());
  }
}

使用区别对比

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

综合使用示例

dart 复制代码
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

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

相关推荐
ss2734 小时前
手写Spring第7弹:Spring IoC容器深度解析:XML配置的完整指南
java·前端·数据库
前端拿破轮5 小时前
从0到1搭一个monorepo项目(二)
前端·javascript·面试
止观止5 小时前
XSS 攻击详解:原理、类型与防范策略
前端·xss
用户47949283569155 小时前
用|运算符写管道?Symbol.toPrimitive让JavaScript提前用上|>语法
前端·javascript
知识分享小能手5 小时前
uni-app 入门学习教程,从入门到精通,uni-app 基础知识详解 (2)
前端·javascript·windows·学习·微信小程序·小程序·uni-app
文心快码BaiduComate5 小时前
限时集福!Comate挂件/皮肤上线,符(福)气掉落中~
前端·后端·程序员
勇敢di牛牛5 小时前
vue3 + mars3D 三分钟画一个地球
前端·vue.js
IT_陈寒6 小时前
Python+AI实战:用LangChain构建智能问答系统的5个核心技巧
前端·人工智能·后端
袁煦丞6 小时前
MoneyPrinterTurbo一键生成短视频:cpolar内网穿透实验室第644个成功挑战
前端·程序员·远程工作