如何使用密钥从 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("");
}
},
);
我正在使用 Future
和 FutureBuilder
而不是 Stream
和 StreamBuilder
,因为必须使用多个 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'];
}
我正在尝试使用 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("");
}
},
);
我正在使用 Future
和 FutureBuilder
而不是 Stream
和 StreamBuilder
,因为必须使用多个 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'];
}