【发布时间】:2021-03-10 16:26:01
【问题描述】:
我试图达到的结果
我将 Firestore 服务器用作餐厅订单系统的 Flutter 应用程序的后端。在实现显示数据库实时更改的屏幕时,我发现自己遇到了一种挑战。
所以我有一个名为“tables”的集合,其中包含多个文档(对于不同的餐厅可能有不同的名称)。这些文档中的每一个都有几个字段,例如'登记时间'。此外,每个表格文档还有一个订单集合(“子集合”)。
我想在我的 Flutter 屏幕上获得一个Stream,它接收所有表格,包括他们的订单。屏幕应该提供所有表格的概览,这意味着它应该随时更新......
- 创建了一个新的表格文档
- 表格文档已更新
- 一个新订单被放置到一个表格子集合“订单”中
- 订单文档已更新
我有一个 Order 和 Table 类的模型。 Table 类的实例应该有一个List<Order> _orders,其中包含存储在子集合中的所有订单。 Stream 应该为我提供List<Table>。
注意:我尝试使用 Flutter Web 来实现这一点,但这可能对我的问题的解决方案没有影响。
当前进度
到目前为止,我实际上几乎解决了这个挑战。
我设法将所有表格中的Stream 显示在屏幕上。
在大多数情况下,它最初会成功加载数据。有时第一次加载屏幕会导致无限加载 CircularProgressIndicator 并且没有加载数据。
当另一个表添加到集合中时,屏幕会更新。添加订单不会立即生效,但如果我第二次重新加载页面或在我将另一个订单添加到订单子集合之后,它会起作用。似乎错误的发生与时间无关。
似乎更新订单文档根本不起作用/重新加载订单状态。
在任何情况下都没有错误消息。
当前代码
TableInfo 的 Stream 类:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart' as m;
import '../../../../../domain/infrastructure/firestore_infrastructure.dart';
import '../../../../../domain/models/orders_models/order.dart';
import '../../../../../domain/models/orders_models/table.dart';
import '../../../../../locator.dart';
///Class for getting all tables and orders in realtime and managing the current filter settings.
class TableInfo extends m.ChangeNotifier {
///List of all tables
List<Table> _tables = [];
///List of all orders
List<Order> _orders = [];
final CollectionReference _tableCollection = locator<CustomFirestore>().tablesRef;
Stream<QuerySnapshot> get _stream => _tableCollection.snapshots();
///returns all tables in the Firestore `tables` collection of the restaurant.
///Also sets a listener for the orders and matches them from the [_orders] list.
Stream<List<Table>> getTableStream(
{void Function(Order savedOrder) onOrderSaved}) async* {
print("Reloading");
final tableDocs = await _tableCollection.get().then((value) => value.docs);
for (QueryDocumentSnapshot tableDoc in tableDocs) {
_tableCollection
.doc(tableDoc.id)
.collection(CustomFirestore.ORDERS_COLLECTION)
.snapshots()
.listen((snapshot) =>
_saveOrders(snapshot, tableDoc.id, onOrderSaved: onOrderSaved));
}
final tableStream =
_stream.map((snapShot) => _unfilteredTablesFromTableSnapshot(snapShot));
yield* tableStream;
}
///Help function to get all tables with ordes from a table doc snapshot.
List<Table> _unfilteredTablesFromTableSnapshot(QuerySnapshot snapShot) {
return snapShot.docs.map((tableDocument) {
final table = Table.fromSnapshot(tableDocument);
table.addOrders(_orders.where((element) => element.tableId == table.id),
overwrite: true);
_tables.add(table);
return table;
}).toList();
}
///Function to save the orders from a snapshot of an order collection of a single table.
void _saveOrders(QuerySnapshot orderCollectionSnapshot, String tableId,
{void Function(Order savedOrder) onOrderSaved}) {
final orderDocs = orderCollectionSnapshot.docs;
print(
"Saving ${orderDocs.length} orders for $tableId at ${DateTime.now()} :)");
for (QueryDocumentSnapshot orderDoc in orderDocs) {
final order = Order.fromSnapshot(orderDoc, tableId);
final existing = _orders.firstWhere(
(element) => element.orderId == order.orderId,
orElse: () => null);
if (existing != null) _orders.remove(existing);
_orders.add(order);
if (onOrderSaved != null) onOrderSaved(order);
}
}
}
如您所见,我首先获取所有表格文档,然后为子集合“orders”添加一个侦听器,称为_saveOrders。似乎代码首先获取订单,这就是为什么我使用 _orders = [] 初始化 Table.fromSnapshot 中的表,然后在 _unfilteredTablesFromTableSnapshot 方法中添加订单列表中的订单。
这是屏幕build 方法的相关部分(显然是放置在脚手架内):
final tableInfo = Provider.of<TableInfo>(context);
return StreamProvider.value(
value: tableInfo.getTableStream(),
catchError: (ctx, error) {
print("Error occured! $error");
print(error.runtimeType);
//throw error;
return [
t.Table(
id: 'fehler',
name: 'Fehler $error',
status: t.TableStatus.Free,
orders: [],
checkinTime: DateTime.now())
];
},
builder: (context, child) {
final allUnfilteredTables = Provider.of<
List<t.Table>>(
context); //corresponds to TableInfo.filteredTableStream
///while the Tables are loading:
if (allUnfilteredTables == null)
return Center(
child: CircularProgressIndicator());
///if there are no tables at all:
if (allUnfilteredTables.isEmpty)
return Text(
"No tables detected.");
///if an error occured:
if (allUnfilteredTables[0].id == 'fehler')
return Text(allUnfilteredTables[0].name);
return GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: (50 / 1920) *
constraints.maxWidth,
crossAxisCount: isTablet ? 3 : 6),
itemCount: allUnfilteredTables.length,
itemBuilder: (ctx, index) {
t.Table table = allUnfilteredTables[index];
return TableMiniDisplay(table);
},
);
});
我已经尝试过的
我已经阅读了很多有关相关问题的信息,但找不到任何对我有很大帮助的答案,以解决我剩余的错误。我还尝试使用 rxdart 和 CombinedStream 但无法运行。
实时同步的主要部分有效,所以我想没有什么能阻止我成功,我感谢你们花时间阅读我的问题的每一个人。我感谢任何可以帮助我的想法或代码示例。
(另外,如果您有任何其他改进代码的建议,请随时发表评论:))
提前谢谢你!
干杯,大卫
【问题讨论】:
标签: firebase flutter dart google-cloud-firestore flutter-layout