简体   繁体   English

flutter bloc - 错误状态从 bloc 发送到小部件

[英]flutter bloc - wrong state is sent from bloc to widget

It seems like bug in bloc v0.11.2这似乎是 bloc v0.11.2 中的错误

I have the following Event/State:我有以下事件/状态:

class DeleteReceipt extends ReceiptEvent {
  final Receipt receipt;
  DeleteReceipt(this.receipt) : super([receipt]);
}

class ReceiptDeleted extends ReceiptState {
  final Receipt receipt;
  ReceiptDeleted(this.receipt) : super();
}

and the following code in bloc:和以下代码块:

if (event is DeleteReceipt) {
  var delReceipt = event.receipt;
  await _receiptDao.delete(delReceipt);
  print("deleting: " + delReceipt.snapshot.documentID);
  yield ReceiptDeleted(delReceipt);
}

and my widget I have:我的小部件有:

      if (state is ReceiptDeleted) {
        print("delete: "+state.receipt.snapshot.documentID);
        receipts.delete(state.receipt);
      }

and when I do: _receiptBloc.dispatch(DeleteReceipt(receipt));当我这样做时: _receiptBloc.dispatch(DeleteReceipt(receipt)); the first time I get:我第一次得到:

I/flutter (28196): deleting: AzgAzcn5wRNFVd7NyZqQ
I/flutter (28196): delete: AzgAzcn5wRNFVd7NyZqQ

which is correct, but the second time I do _receiptBloc.dispatch(DeleteReceipt(receipt));这是正确的,但我第二次做_receiptBloc.dispatch(DeleteReceipt(receipt)); on a different receipt, I get:在不同的收据上,我得到:

I/flutter (28196): deleting: d4oUjrGwHX1TvIDr9L2M
I/flutter (28196): delete: AzgAzcn5wRNFVd7NyZqQ

You can see that in the second time the DeleteReceipt event was received with the correct value, but the ReceiptDeleted State was received with the wrong value, and then it just get stuck like this, it never fires ReceiptDeleted State with the correct value, only with the first value.您可以看到,在第二次接收到带有正确值的DeleteReceipt事件时,但是接收到带有错误值的ReceiptDeleted状态,然后它就这样卡住了,它从不使用正确值触发ReceiptDeleted状态,只有第一个值。

My app is not trivial, and I have set many events and state in the past, and it worked with no issue (except this one, that probably is related flutter bloc state not received )我的应用程序不是微不足道的,我过去设置了许多事件和状态,并且它没有问题(除了这个,可能是相关的flutter bloc state not received

Basically I let the user create photos of receipt, that are persistent (using bloc/firestore), and I want to let the user delete them, so when the user click on a receipt, it opens in a new screen:基本上我让用户创建收据的照片,这些照片是持久的(使用 bloc/firestore),我想让用户删除它们,所以当用户点击收据时,它会在一个新屏幕中打开:

Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) {
      return ReceiptDetailPage(widget.receipt);
    },
  ),

and when the user click on delete, I show a dialog, and delete the receipt if is OK当用户点击删除,我将展示一个对话框,如果是删除收据OK

var result = await showDialog(
  context: context,
  builder: (BuildContext dialogCtxt) {
    // return object of type Dialog
    return AlertDialog(
      title: new Text(AppLocalizations.of(context).deleteReceiptQuestion),
      actions: <Widget>[
        // usually buttons at the bottom of the dialog
        new FlatButton(
          child: new Text(AppLocalizations.of(context).cancel),
          onPressed: () {
            Navigator.of(dialogCtxt).pop("cancel");
          },
        ),
        new FlatButton(
          child: new Text(AppLocalizations.of(context).ok),
          onPressed: () {
            Navigator.of(dialogCtxt).pop("OK");
          },
        ),
      ],
    );
  },
);
if (result == 'OK') {
  Navigator.of(context).pop();
  _receiptBloc.dispatch(DeleteReceipt(receipt));
}

Solution:解决方案:

add state/event:添加状态/事件:

class EmptyState extends ReceiptState {}
class EmptyEvent extends ReceiptEvent {}

after receiving the delete state do:收到删除状态后,请执行以下操作:

      if (state is ReceiptDeleted) {
        print("delete: "+state.receipt.snapshot.documentID);
        receipts.delete(state.receipt);
        _receiptBloc.dispatch(EmptyEvent()); // add this line
      }

and add this to your bloc并将其添加到您的集团

if (event is EmptyEvent) {
  yield EmptyState();
}

This will cause an empty event and state to be fired and will clear the problem这将导致触发空事件和状态,并将清除问题

Explain: I noticed that once I fire a State, the block provider will send that state every time I change a screen, which is strange since the app is receiving a Delete State many time.解释:我注意到一旦我触发一个状态,块提供者会在我每次更改屏幕时发送该状态,这很奇怪,因为应用程序多次收到Delete状态。 this is not a problem in my case, since the code will try to delete an element that is already delete and will fail quietly:在我的情况下,这不是问题,因为代码将尝试删除一个已经被删除的元素,并且会安静地失败:

  void delete(Receipt receipt) {
    try {
      Receipt oldReceipt = receipts.firstWhere(
          (r) => r.snapshot.documentID == receipt.snapshot.documentID);
      receipts.remove(oldReceipt);
    } catch (e) {
      print(e);
    }
  }

NOTE: this seems to happen with all State that the app is firing, not only the Delete state注意:这似乎发生在应用程序正在触发的所有状态中,而不仅仅是Delete状态

So I guest that if I will fire an empty event, it will clear the old Delete state, and will somehow fix the issue, and WALLA...所以我说,如果我触发一个空事件,它会清除旧的Delete状态,并会以某种方式解决问题,WALLA...

Note that I didn't had to actually listen to the EmptyState any where in my code请注意,我不必在代码中的任何地方实际收听EmptyState

MORE INFO:更多信息:

I realize that although the bloc seems to loose state, also my design is wrong, because the Data Structure should be updated in the bloc , once the event is received and not in the widget, when the state is received (or not received in this case, which cause the bug)我意识到虽然bloc似乎松散了状态,但我的设计也是错误的,因为数据结构应该在bloc更新,一旦接收到事件而不是在小部件中,当接收到状态(或未在此情况,导致错误)

Initially I used bloc with sembast , but then I wanted the data to be sync with the remote DB, so I replaced sembast with firestore .最初我使用blocsembast ,但后来我希望数据与远程数据库同步,所以我用firestore替换了sembast

but that causes the load time to go from nothing, to more than 2 seconds, and that is a problem since in the original design I load all the data from the DB on every update.但这会导致加载时间从零变为 2 秒以上,这是一个问题,因为在原始设计中,我在每次更新时都从数据库加载所有数据。

So I tried to update the store and the UI seperatly, ie.所以我尝试单独更新store和用户界面,即。 instead of reading all the data, I keep a List in my widget and update the widget when the state changes - per update/delete state.我没有读取所有数据,而是在我的小部件中保留一个列表,并在状态更改时更新小部件 - 每个更新/删除状态。

That was a problem, since many state were lost (especially when the user click fast - which cause many events/states to fire)这是一个问题,因为许多状态丢失了(尤其是当用户快速点击时 - 这会导致许多事件/状态触发)

So I guess a correct solution would be to manage the in-memory Data in a separate Service, and update the Data when the Event is received, and then read all data from the Service instead of the store (when possible)所以我想一个correct解决方案是在单独的服务中管理内存数据,并在收到事件时更新数据,然后从服务而不是存储中读取所有数据(如果可能)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM