Cod*_*vid 5 dart firebase flutter google-cloud-firestore flutter-layout
我试图达到的结果
我使用 Firestore 服务器作为餐厅订单系统 Flutter 应用程序的后端。我发现自己在实现显示数据库实时更改的屏幕时遇到了挑战。
所以我有一个名为“tables”的集合,其中包含多个文档(不同的餐厅可能有不同的名称)。每个文档都有几个字段,例如“checkinTime”。此外,每个表文档还具有订单集合(“子集合”)。
我想Stream
在我的 Flutter 屏幕上看到一个接收所有表格的屏幕,包括他们的订单。屏幕应该给出所有表格的概述,这意味着它应该在任何时候更新......
Order
我有和类的模型Table
。Table 类的实例应该有一个List<Order> _orders
包含子集合中存储的所有订单的 。应该Stream
为我提供List<Table>
.
注意:我尝试使用 Flutter Web 来实现这一点,但这对于解决我的问题可能没有什么影响。
现在的进展
到目前为止,我实际上已经几乎解决了这个挑战。我已经设法将Stream
所有表格显示到屏幕上。
在大多数情况下,它最初会成功加载数据。有时,第一次加载屏幕会导致无限加载 CircularProgressIndicator 并且未加载数据。
当另一个表添加到集合中时,屏幕会更新。添加订单不会立即起作用,但如果我再次重新加载页面或在将另一个订单添加到订单子集合后,它就会起作用。似乎错误的发生与时间无关。
更新订单文档似乎根本不起作用/重新加载订单状态。
任何情况下都不会出现错误消息。
当前代码
TableInfo
流类:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我首先获取所有表文档,然后为子集合“orders”添加一个名为 的侦听器_saveOrders
。看起来代码首先获取订单,这就是为什么我使用Table.fromSnapshot
_orders = [] 初始化表,然后从方法内的订单列表添加订单_unfilteredTablesFromTableSnapshot
。
这是 screenbuild
方法的相关部分(显然,放置在脚手架中):
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);
},
);
});
Run Code Online (Sandbox Code Playgroud)
我已经尝试过的
我已经阅读了很多相关问题,但找不到任何对我有很大帮助的答案来解决我剩余的错误。我还尝试使用rxdart和 CombinedStream 但无法运行。
实时同步的主要部分是有效的,所以我想几乎没有什么阻碍我成功,我感谢所有花时间阅读我的问题的人。我感谢任何可以帮助我的想法或代码示例。
(此外,如果您有任何其他改进代码的建议,请随时发表评论:))
先感谢您!
干杯,大卫
I\xc2\xb4ll 建议您使用两个Streambuilder包装您的小部件。只需使用第一个来流式传输表,使用第二个来访问每个表内的订单。不要\xc2\xb4t 每次要访问数据时都添加 Streambuilder。仅使用两个流生成数据并将其传递给变量。
\n\n这是示例代码:
\n数据库.dart
\nimport \'package:cloud_firestore/cloud_firestore.dart\';\n\nclass Tables {\n CollectionReference tablesReference =\n FirebaseFirestore.instance.collection("tables");\n\n Stream<QuerySnapshot> getTables() {\n // Returns all tables\n return tablesReference.snapshots();\n }\n\n Stream<QuerySnapshot> getOrdersFromTables(String tableName) {\n // Returns specific table orders\n return tablesReference.doc(tableName).collection("orders").snapshots();\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n主程序.dart
\nimport \'package:firebase_core/firebase_core.dart\';\nimport \'package:flutter/material.dart\';\nimport \'tables.dart\';\n\n\nvoid main() {\n runApp(MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return MaterialApp(\n debugShowCheckedModeBanner: false,\n theme: ThemeData.dark(),\n // Initialize FlutterFire:\n home: FutureBuilder(\n future: Firebase.initializeApp(),\n builder: (context, snapshot) {\n // Check for errors\n if (snapshot.hasError) {\n return Center(\n child: Text("Error"),\n );\n }\n\n // Once complete, show your application\n if (snapshot.connectionState == ConnectionState.done) {\n return TablesPage();\n }\n\n // Otherwise, show something whilst waiting for initialization to complete\n return Center(\n child: CircularProgressIndicator(),\n );\n }),\n );\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n表.dart
\nimport \'package:cloud_firestore/cloud_firestore.dart\';\nimport \'package:flutter/material.dart\';\nimport \'package:intl/intl.dart\';\nimport \'orders.dart\';\nimport \'database.dart\';\n\nclass TablesPage extends StatefulWidget {\n @override\n _TablesPageState createState() => _TablesPageState();\n}\n\nclass _TablesPageState extends State<TablesPage> {\n @override\n Widget build(BuildContext context) {\n // Get Tables\n return Scaffold(\n appBar: AppBar(\n title: Text("Tables"),\n ),\n body: StreamBuilder<QuerySnapshot>(\n stream: Tables().getTables(),\n builder: (context, tables) {\n if (tables.hasError)\n return Text(tables.error);\n else if (tables.hasData) {\n if (tables.data.docs.isEmpty) {\n return Text("No tables.");\n } else {\n return ListView.builder(\n itemCount: tables.data.docs.length,\n itemBuilder: (context, index) {\n // Get Orders\n return StreamBuilder<QuerySnapshot>(\n stream: Tables()\n .getOrdersFromTables(tables.data.docs[index].id),\n builder: (context, orders) {\n if (orders.hasData) {\n return ListTile(\n title:\n Text("Table ${tables.data.docs[index].id}"),\n subtitle: Text(DateFormat("HH:mm").format(tables\n .data.docs[index]["checkinTime"]\n .toDate())),\n trailing: Text(\n "Total orders: ${orders.data.docs.length.toString()}"),\n onTap: () {\n // Navigate to Orders Page\n Navigator.push(\n context,\n MaterialPageRoute(\n builder: (context) => Orders(\n orders: orders.data.docs,\n ),\n ),\n );\n });\n } else {\n return Container();\n }\n });\n },\n );\n }\n } else {\n print(tables\n .connectionState); //is stuck in ConnectionState.waiting or ConnectionState.active\n return Center(child: CircularProgressIndicator());\n }\n },\n ),\n );\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n订单.dart
\nimport \'package:cloud_firestore/cloud_firestore.dart\';\nimport \'package:flutter/material.dart\';\n\nclass Orders extends StatelessWidget {\n const Orders({\n Key key,\n @required this.orders,\n }) : super(key: key);\n\n final List<QueryDocumentSnapshot> orders;\n\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: Text("Orders"),\n ),\n body: ListView.builder(\n itemCount: orders.length,\n itemBuilder: (context, index) {\n return ListTile(\n title: Text(orders[index].id),\n subtitle: Text(orders[index]["barStatus"]),\n leading: Column(\n mainAxisAlignment: MainAxisAlignment.center,\n children: [\n Text((index+1).toString()),\n ],\n ),\n );\n },\n ),\n );\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
归档时间: |
|
查看次数: |
2570 次 |
最近记录: |