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

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

相关推荐
元直数字电路验证1 分钟前
Jakarta EE Web 聊天室技术梳理
前端
wadesir4 分钟前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛4 分钟前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端
灵犀坠6 分钟前
前端面试八股复习心得
开发语言·前端·javascript
9***Y487 分钟前
前端动画性能优化
前端
网络点点滴9 分钟前
Vue3嵌套路由
前端·javascript·vue.js
牧码岛20 分钟前
Web前端之Vue+Element打印时输入值没有及时更新dom的问题
前端·javascript·html·web·web前端
小二李27 分钟前
第8章 Node框架实战篇 - 文件上传与管理
前端·javascript·数据库
HIT_Weston43 分钟前
45、【Ubuntu】【Gitlab】拉出内网 Web 服务:http.server 分析(二)
前端·http·gitlab
十一.3661 小时前
79-82 call和apply,arguments,Date对象,Math
开发语言·前端·javascript