如何使用密钥从 Firestore 加载数据

How to load data from Firestore using keys

我正在尝试使用 Flutter 和 Firestore 构建电子商务应用程序,但在构建购物车时遇到了挑战。使用下面的代码,我已经能够使用产品 ID 获得用户希望添加到购物车的产品。我的挑战是如何使用 id 或键从 Firestore 获取产品的详细信息并将购物车产品存储在 Firestore 或 SQLite 中,以便我可以从那里查询并将它们显示在购物车页面上。

appstate.dart


class AppStateModel extends Model {

  final Map<int, int> _productsInCart = <int, int>{};

  Map<int, int> get productsInCart => Map<int, int>.from(_productsInCart);



  void addProductToCart(int productId) {
    if (!_productsInCart.containsKey(productId)) {
      _productsInCart[productId] = 1;
    } else {
      _productsInCart[productId]++;
    }

    notifyListeners();
  }


  void removeItemFromCart(int productId) {
    if (_productsInCart.containsKey(productId)) {
      if (_productsInCart[productId] == 1) {
        _productsInCart.remove(productId);
      } else {
        _productsInCart[productId]--;
      }
    }

    notifyListeners();
  }

  void clearCart() {
    _productsInCart.clear();
    notifyListeners();
  }


}

product_display.dart 带有 onPressed 函数的页面,用于获取点击添加到购物车的商品的 ID

  CupertinoActionSheetAction(
    child: const Text('Add To Cart'),
      onPressed: () {
   model.addProductToCart(products[index].id);
  },
)

product.dart

class Products{
 final  String category;
 final  String description;
 final  int id;
 final int  price;
 final  String title;
 final  String url;


 const Products( {this.category,this.description,this.id, this.price, this.title, this.url,
 });


}

CartProduct.dart

class CartProducts{
 final  String category;
 final  String description;
 final  int id;
 final int  price;
 final  String title;
 final  String url;


 const CartProducts( {this.category,this.description,this.id, this.price, this.title, this.url,
 });


}

现在假设我在产品购物车中有 ID 为 1、4、6、9、11 的产品,当我使用 print(model.productsInCart.keys) 在控制台中打印时,这是输出 (1, 4 , 6, 9, 11), 现在我的挑战是如何使用这些 ID 从 Firestore 集合产品中查询 ID 为 1,4,6,9,11 的产品,并将它们存储在 Firebase 或 SQLite 中,以便我可以显示它们在购物车页面中供用户查看购物车中的 his/her 件商品。

我想这就是你想要做的

Firestore.instance.collection("collection").document("id").get().then((querySnapshot){
    print(querySnapshot.data);
  });

显然将 collection 替换为您的 collection 名称,将 id 替换为您要获取的 ID。这里我使用 .then 语法,但你可以像往常一样将 .then 之前的所有内容传递给 FutureBuilder

编辑: 您需要添加一个辅助方法来从 firestore 中获取所有数据。

Future<List<Products>> getCollection() async {
  List<int> idList = [];
  // productsInCart[key] = value; where key is id and value is amount in cart
  productsInCart.forEach((key, value) {
    idList.add(key);
  });
  List<Products> productList = [];
  for (var id in idList) {
    var documents = (await Firestore.instance
            .collection('products')
            .where('id', isEqualTo: id)
            .getDocuments())
        .documents;
    if (documents.length > 0) {
      var doc = documents[0]
          .data; // if there are multiple documents with given id get first document
      var prod = Products(
          id: doc['id'],
          title: doc['title'],
          price: doc['price'],
          category: doc['category'],
          description: doc['description']);
      productList.add(prod);
    }
  }
  return productList;
}

然后使用 FutureBuilder 构建列表

FutureBuilder<List<Products>>(
  future: getCollection(),
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      var list = snapshot.data;
      return ListView.builder(
          itemCount: list.length,
          itemBuilder: (context, index) => ListTile(
                title: Text("${list[index].title}"),
                subtitle: Text(
                    "Amount in cart : ${productsInCart[list[index].id]}"),
              ));
    } else {
      return Text("");
    }
  },
);

我正在使用 FutureFutureBuilder 而不是 StreamStreamBuilder,因为必须使用多个 id 进行查询在云 firestore 中是一项繁琐的任务,因为 firestore Logical OR 没有官方支持。因此,必须从多个流源收集数据很困难。只要在使用应用程序时未更改产品详细信息,使用 FutureBuilder 的输出与使用 StreamBuilder 的输出相同。

编辑 2: 要使用多个流源,请使用 async 包中的 StreamGroup。这是最终代码的样子

Stream<List<Products>> _getCollection() async* {
  List<int> idList = [];
  // productsInCart[key] = value; where key is id and value is amount in cart
  productsInCart.forEach((key, value) {
    idList.add(key);
  });
  StreamGroup<QuerySnapshot> streamGroup = StreamGroup();
  for (var id in idList) {
    var stream = Firestore.instance
        .collection('products')
        .where('id', isEqualTo: id)
        .snapshots();
    streamGroup.add(stream);
  }
  //using map to store productDetails so that same products from multiple stream events don't get added multiple times.
  Map<int, Products> productMap = {};
  await for (var val in streamGroup.stream) {
    var documents = val.documents;
    var doc = documents[0].data;
    var product = Products.fromMap(doc);
    productMap[product.id] = product;
    yield productMap.values.toList();
  }
}
StreamBuilder<List<Products>>(
  stream: _getCollection(),
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      var values = snapshot.data;
      return ListView.builder(
          itemCount: values.length,
          itemBuilder: (context, index) => ListTile(
                title: Text(values[index].title),
                subtitle: Text(
                    "Amount in cart : ${productsInCart[values[index].id]}"),
              ));
    } else {
      print("There is no data");
      return Text("");
    }
  },
),

为方便起见,我添加了命名构造函数

Products.fromMap(Map map) {
  this.id = map['id'];
  this.title = map['title'];
  this.description = map['description'];
  this.price = map['price'];
  this.category = map['category'];
}