You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中实现Firestore用户个性化订阅,避免全局数据冲突

解决方案

核心思路是将商品公共数据与用户个性化订阅数据分离存储,避免全局修改商品集合导致的冲突。具体实现步骤如下:

1. 调整Firestore数据结构

  • 保留原products集合:仅存储商品的公共信息(如名称、价格、图片URL等),所有用户共享读取权限,禁止普通用户修改该集合内容(仅管理员可维护)。
  • 新增user_subscriptions集合:每个文档对应一个用户,文档ID直接使用Firebase Auth生成的用户UID(确保唯一且与用户绑定)。文档内用数组subscribedProductIds存储用户订阅的商品ID列表。

2. 设置Firestore安全规则

确保用户只能读写自己的订阅数据,防止越权操作:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // 用户仅能访问自己的订阅文档
    match /user_subscriptions/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
    // 所有用户可读取商品信息,禁止普通用户修改
    match /products/{productId} {
      allow read: if true;
      allow write: if false; // 管理员权限可单独配置
    }
  }
}

3. 实现订阅/取消订阅逻辑

获取当前用户UID

先确保用户已登录,获取其唯一标识:

final currentUser = FirebaseAuth.instance.currentUser;
if (currentUser == null) {
  // 处理未登录场景,如跳转登录页
  return;
}
final userId = currentUser.uid;

订阅商品

向当前用户的订阅文档中添加商品ID:

Future<void> subscribeProduct(String productId) async {
  final userId = FirebaseAuth.instance.currentUser?.uid;
  if (userId == null) return;

  final subDocRef = FirebaseFirestore.instance.collection('user_subscriptions').doc(userId);
  
  // 尝试更新数组,若文档不存在则创建
  await subDocRef.update({
    'subscribedProductIds': FieldValue.arrayUnion([productId])
  }).onError((error, stackTrace) {
    if (error.code == 'not-found') {
      return subDocRef.set({
        'subscribedProductIds': [productId]
      });
    }
    throw error;
  });
}

取消订阅商品

从当前用户的订阅文档中移除商品ID:

Future<void> unsubscribeProduct(String productId) async {
  final userId = FirebaseAuth.instance.currentUser?.uid;
  if (userId == null) return;

  await FirebaseFirestore.instance.collection('user_subscriptions').doc(userId).update({
    'subscribedProductIds': FieldValue.arrayRemove([productId])
  });
}

4. 获取并展示用户订阅列表

先获取用户订阅的商品ID,再根据ID批量查询商品详情:

Future<List<Map<String, dynamic>>> getSubscribedProducts() async {
  final userId = FirebaseAuth.instance.currentUser?.uid;
  if (userId == null) return [];

  final subDoc = await FirebaseFirestore.instance.collection('user_subscriptions').doc(userId).get();
  if (!subDoc.exists) return [];

  final subscribedIds = subDoc.data()?['subscribedProductIds'] as List? ?? [];
  if (subscribedIds.isEmpty) return [];

  // 根据ID批量获取商品信息
  final productsSnapshot = await FirebaseFirestore.instance
      .collection('products')
      .where(FieldPath.documentId, whereIn: subscribedIds)
      .get();

  return productsSnapshot.docs.map((doc) => doc.data()).toList();
}

在ListView中使用时,直接调用该方法获取数据并构建列表项即可,每个用户看到的都是自己的订阅内容。

内容的提问来源于stack exchange,提问作者Ronin Spect

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 18:35:51