【问题标题】:Only update UI when there are changes in the server, using streambuilder仅在服务器发生更改时更新 UI,使用 streambuilder
【发布时间】:2021-07-06 14:41:34
【问题描述】:

我正在使用 streambuilder 来检查是否下了新订单。 我正在检查订单状态,如果订单状态未知,我想显示一个弹出窗口,效果很好。但如果我没有选择更新订单状态的选项,streambuilder 会在几秒钟后刷新,并在其顶部显示另一个弹出窗口。

获取订单功能:

Future<Orders> getOrders() async {
    String bsid = widget.bsid;
    try {
      Map<String, dynamic> body = {
        "bsid": bsid,
      };
      http.Response response = await http.post(
          Uri.parse(
              "**API HERE**"),
          body: body);

      Map<String, dynamic> mapData = json.decode(response.body);

      Orders myOrders;
      print(response.body);

      if (response.statusCode == 200) {
        print("Success");
        myOrders = Orders.fromJson(mapData);
      }

      return myOrders;
    } catch (e) {}
  }

这里是流函数:

 Stream<Orders> getOrdersStrem(Duration refreshTime) async* {
while (true) {
  await Future.delayed(refreshTime);
  yield await getOrders();
}}

StreamBuilder:

StreamBuilder<Orders>(
            stream: getOrdersStrem(
              Duration(
                seconds: 2,
              ),
             
            ),
            builder: (context, snapshot) {
              if (!snapshot.hasData) {
                return Center(
                     CircularProgressIndicator.adaptive(),
                );
              }
              var orders = snapshot.data.statedatas;

              return ListView.builder(
                itemCount: orders.length,
                itemBuilder: (BuildContext context, int index) {
                  var orderResponse =
                      snapshot.data.statedatas[index].strAccept;
                  print(orderResponse);

                  if (orderResponse == "0") {
                    print("order status unknown");

                    Future.delayed(Duration.zero, () {
                      _playFile();
                      showCupertinoDialog(
                        context: context,
                        builder: (ctx) => AlertDialog(
                          title: Center(
                            child: Text(
                              "#${orders[index].ordrAutoid}",
                            ),
                          ),
                          content: Row(
                            children: [
                              SizedBox(
                                width: 120,
                                child: ElevatedButton(
                                  style: ButtonStyle(
                                    backgroundColor: MaterialStateProperty
                                        .resolveWith<Color>(
                                      (Set<MaterialState> states) {
                                        if (states.contains(
                                            MaterialState.pressed))
                                          return Colors.black;
                                        return Colors
                                            .green; // Use the component's default.
                                      },
                                    ),
                                  ),
                                  onPressed: () async {
                                    _stopFile();
                                    Navigator.pop(context);
                                    await changeOrderStatus(
                                        orders[index].orid, "accept");

                                    // setState(() {});
                                  },
                                  child: Text('Accept'),
                                ),
                              ),

                              SizedBox(
                                width: 15,
                              ),
                              SizedBox(
                                width: 120,
                                child: ElevatedButton(
                                  style: ButtonStyle(
                                    backgroundColor: MaterialStateProperty
                                        .resolveWith<Color>(
                                      (Set<MaterialState> states) {
                                        if (states.contains(
                                            MaterialState.pressed))
                                          return Colors.black;
                                        return Colors
                                            .red; // Use the component's default.
                                      },
                                    ),
                                  ),
                                  onPressed: () async {
                                    _stopFile();
                                    Navigator.pop(context);
                                    await changeOrderStatus(
                                        orders[index].orid, "Reject");

                                    // setState(() {});
                                  },
                                  child: Text('Reject'),
                                ),
                              ),
                              // TextButton(
                              //   onPressed: () async {
                              //     _stopFile();
                              //     Navigator.pop(context);
                              //     await changeOrderStatus(
                              //         orders[index].orid, "reject");
                              //   },
                              //   child: Text('reject'),
                              // ),
                            ],
                          ),
                        ),
                      );
                    }).then((value) {
                      _stopFile();
                      print("ENDING");
                    });
                  }
return Container();

【问题讨论】:

  • 请发布您的 'getOrders()` 函数。
  • @HuthaifaMuayyad 我在问题中编辑了 getOrders() 函数。看看

标签: flutter stream-builder


【解决方案1】:

根据我的阅读内容,您每两秒查询一次 API。
每次 API 响应时,您都会将新数据推送到 StreamBuilder,这解释了为什么会有多个弹出窗口堆叠。

解决您的问题的一个简单方法是在显示对话框时将布尔值设置为 true,以避免多次显示。

bool isDialogShowing = false;
...
if (orderResponse == "0" && !isDialogShowing) {
  isDialogShowing = true;
...

但是你的代码中有一些你应该避免的错误:

  • 无限循环
  • 自动多次查询您的 API(如果有大量用户同时使用您的应用,它可能会 DDOS 您的服务)
  • 在 ListView 构建器中显示对话框

【讨论】:

  • 感谢伙计,这项工作就像一个魅力,也感谢您指出错误。我会调查的...
【解决方案2】:

您不应该从您的build 方法调用showCupertinoDialog(可能还有_playFile())。用Future.delayed(Duration.zero, () { ... }) 包装它可能是框架给出的错误的解决方法。

build 方法可以多次执行。您可能想要一种方法来运行 _playFile 并显示不依赖于 UI 的对话框。我不认为StreamBuilder 是解决这个问题的正确方法。

您可以使用StatefulWidget 并在来自initState 方法的流上执行listeninitState 只会被调用一次。

【讨论】:

  • 我想这看起来像是一个更合法的答案,但似乎我必须围绕我的代码做很多工作。但我仍然会调查它。谢谢
【解决方案3】:

在 if 语句之外创建一个变量来检查最后一个已知的订单状态,当有新值出现时,先将其与旧值进行比较,然后执行 if 语句逻辑。

//This is outside the stream builder:
String orderResponseCheck = "";
.
.
.
//This is inside your streambuidler, if the orderResponseCheck is still equal to "", the if statement will be executed,
//and the value of orderResponse wil be assigned to it. This will only show the alert dialog if the orderResponse status changes from the one that previously triggered it.

 var orderResponse =snapshot.data.statedatas[index].strAccept;
 print(orderResponse);
 if (orderResponseCheck != orderResponse && orderResponse == "0") {
 orderResponseCheck = orderResponse;
.
.
.
//logic same as before

【讨论】:

  • 感谢 Huthaifa,你们真的是救生员。
猜你喜欢
  • 2016-11-27
  • 1970-01-01
  • 2012-04-23
  • 2020-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多