gpt4 book ai didi

firebase - 在 Flutter 中收听 Firestore 集合及其子集合

转载 作者:行者123 更新时间:2023-12-04 08:30:35 27 4
gpt4 key购买 nike

我努力达到的结果

我将 Firestore 服务器用作餐厅订单系统的 Flutter 应用程序的后端。在实现显示数据库实时更改的屏幕时,我发现自己遇到了一些挑战。

Firestore DB中的结构如下: structure in the Firestore DB

所以我有一个名为“表格”的集合,其中包含多个文档(不同的餐厅可能有不同的名称)。这些文档中的每一个都有几个字段,例如'登记时间'。此外,每个表文档还有一个订单集合(“子集合”)。

我想要一个Stream在我的 Flutter 屏幕上,它接收所有表格,包括他们的订单。屏幕应该给出所有表格的概览,这意味着它应该在任何时候更新...

  • 创建了一个新的表格文件
  • 表格文档已更新
  • 新订单被放入表子集合'orders'
  • 订单文件已更新

我有一个模型 OrderTable类(class)。 Table 类的一个实例应该有一个 List<Order> _orders其中包含存储在子集合中的所有订单。 Stream应该给我提供 List<Table> .

注意:我尝试使用 Flutter Web 来实现这一点,但这可能不会对我的问题的解决产生影响。

当前进度

到目前为止,我实际上几乎成功地解决了这个挑战。我设法得到一个 Stream将所有表格显示在屏幕上。

在大多数情况下,它最初会成功加载数据。有时屏幕的第一次加载会导致无限加载 CircularProgressIndicator 而数据未加载。

当另一个表被添加到集合中时,屏幕会更新。添加订单不会立即起作用,但如果我第二次重新加载页面或在我将另一个订单添加到订单子(monad)集合后它会起作用。似乎错误的发生与时间无关。

似乎更新订单文档根本不起作用/重新加载订单状态。

在任何情况下都不会出现错误信息。

当前代码

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);
}
}
}

如您所见,我首先获取所有表文档,然后为子集合“订单”添加一个监听器,称为 _saveOrders .看起来代码首先获得订单,这就是我在 Table.fromSnapshot 中初始化表的原因使用 _orders = [] 然后从 _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 但无法运行它。

实时同步的主要部分是有效的,所以我想只有很少的东西能阻止我成功,我感谢大家花时间阅读我的问题。我很感激任何可以帮助我的想法或代码示例。

(此外,如果您有任何其他改进代码的建议,请随时发表评论:))

提前致谢!

干杯,大卫

最佳答案

我建议您使用两个 Streambuilder 包装您的小部件。只需使用第一个来流式传输表,使用第二个来访问每个表中的订单。不要在每次要访问数据时都添加 Streambuilder。仅使用两个流生成数据并通过变量传递。

enter image description here

这是示例代码:

database.dart

import 'package:cloud_firestore/cloud_firestore.dart';

class Tables {
CollectionReference tablesReference =
FirebaseFirestore.instance.collection("tables");

Stream<QuerySnapshot> getTables() {
// Returns all tables
return tablesReference.snapshots();
}

Stream<QuerySnapshot> getOrdersFromTables(String tableName) {
// Returns specific table orders
return tablesReference.doc(tableName).collection("orders").snapshots();
}
}

ma​​in.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'tables.dart';


void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
// Initialize FlutterFire:
home: FutureBuilder(
future: Firebase.initializeApp(),
builder: (context, snapshot) {
// Check for errors
if (snapshot.hasError) {
return Center(
child: Text("Error"),
);
}

// Once complete, show your application
if (snapshot.connectionState == ConnectionState.done) {
return TablesPage();
}

// Otherwise, show something whilst waiting for initialization to complete
return Center(
child: CircularProgressIndicator(),
);
}),
);
}
}

表格.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'orders.dart';
import 'database.dart';

class TablesPage extends StatefulWidget {
@override
_TablesPageState createState() => _TablesPageState();
}

class _TablesPageState extends State<TablesPage> {
@override
Widget build(BuildContext context) {
// Get Tables
return Scaffold(
appBar: AppBar(
title: Text("Tables"),
),
body: StreamBuilder<QuerySnapshot>(
stream: Tables().getTables(),
builder: (context, tables) {
if (tables.hasError)
return Text(tables.error);
else if (tables.hasData) {
if (tables.data.docs.isEmpty) {
return Text("No tables.");
} else {
return ListView.builder(
itemCount: tables.data.docs.length,
itemBuilder: (context, index) {
// Get Orders
return StreamBuilder<QuerySnapshot>(
stream: Tables()
.getOrdersFromTables(tables.data.docs[index].id),
builder: (context, orders) {
if (orders.hasData) {
return ListTile(
title:
Text("Table ${tables.data.docs[index].id}"),
subtitle: Text(DateFormat("HH:mm").format(tables
.data.docs[index]["checkinTime"]
.toDate())),
trailing: Text(
"Total orders: ${orders.data.docs.length.toString()}"),
onTap: () {
// Navigate to Orders Page
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Orders(
orders: orders.data.docs,
),
),
);
});
} else {
return Container();
}
});
},
);
}
} else {
print(tables
.connectionState); //is stuck in ConnectionState.waiting or ConnectionState.active
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}

订单.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

class Orders extends StatelessWidget {
const Orders({
Key key,
@required this.orders,
}) : super(key: key);

final List<QueryDocumentSnapshot> orders;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Orders"),
),
body: ListView.builder(
itemCount: orders.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(orders[index].id),
subtitle: Text(orders[index]["barStatus"]),
leading: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text((index+1).toString()),
],
),
);
},
),
);
}
}

关于firebase - 在 Flutter 中收听 Firestore 集合及其子集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65044932/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com