BlocListener 未更新,原因不明

BlocListener not being updated for unknown reason

提问人:N.K. 提问时间:11/11/2023 最后编辑:N.K. 更新时间:11/15/2023 访问量:20

问:

我在 BLoC 方面遇到了一些非常奇怪的问题。我正在做一些不被认为是最佳实践的事情(在肘之间共享数据),但我相信肘仍然应该按预期工作。

我在 SelectMultipleCubit 中有这个方法 activate():

class SelectMultipleCubit extends Cubit<SelectMultipleState> {
  SelectMultipleCubit(this.folderViewCubit, this.fileExplorerCubit) : super(SelectMultipleInitial());

  final FolderViewCubit folderViewCubit;
  final FileExplorerCubit fileExplorerCubit;

以下是修改后的版本:activate()

void activate(ListItem selectedItem) async {
    emit(SelectMultipleLoading());

    try {
      // Get the parent ID and type of the selected item
      String parentId;
      dynamic selectedObject;

      if (selectedItem.item is Folder) {
        parentId = (selectedItem.item as Folder).parentId;
        selectedObject = selectedItem.item as Folder;
      } else if (selectedItem.item is Receipt) {
        parentId = (selectedItem.item as Receipt).parentId;
        selectedObject = selectedItem.item as Receipt;
      } else {
        emit(SelectMultipleError());
        return;
      }

      List<Object> items = List.from(folderViewCubit.cachedCurrentlyDisplayedFiles);

      // Remove the selected item from the list
      items.removeWhere((element) {
        if (element is Folder && selectedObject is Folder) {
          return element.id == selectedObject.id;
        } else if (element is Receipt && selectedObject is Receipt) {
          return element.id == selectedObject.id;
        }
        return false;
      });

      print('items: ${items.length}');

      // Convert items to ListItems
      List<ListItem> listItems =
          items.map((item) => ListItem(item: item)).toList();

      print('listItems: ${listItems.length}');

      emit(SelectMultipleActivated(
          items: listItems, initiallySelectedItem: selectedItem));
    } on Exception catch (e) {
      print(e.toString());
      emit(SelectMultipleError());
    }
  }

这是其原始版本,不使用来自其他肘的缓存文件。activate()

void activate(ListItem selectedItem) async {
    emit(SelectMultipleLoading());

    try {
      // Get the parent ID and type of the selected item
      String parentId;
      dynamic selectedObject;

      if (selectedItem.item is Folder) {
        parentId = (selectedItem.item as Folder).parentId;
        selectedObject = selectedItem.item as Folder;
      } else if (selectedItem.item is Receipt) {
        parentId = (selectedItem.item as Receipt).parentId;
        selectedObject = selectedItem.item as Receipt;
      } else {
        emit(SelectMultipleError());
        return;
      }

// setting last order & column values
      final String lastColumn = _prefs.getLastColumn();
      final String lastOrder = _prefs.getLastOrder();

      List<Object> items;

      switch (lastColumn) {
        case 'price':
          final List<FolderWithPrice> foldersWithPrice = await DatabaseRepository.instance.getFoldersByPrice(parentId, lastOrder);
          final List<ReceiptWithPrice> receiptsWithPrices = await DatabaseRepository.instance.getReceiptsByPrice(parentId, lastOrder);
          items = [...foldersWithPrice, ...receiptsWithPrices];
          break;
        case 'storageSize':
          final List<FolderWithSize> foldersWithSize = await DatabaseRepository.instance.getFoldersByTotalReceiptSize(parentId, lastOrder);
          final List<ReceiptWithSize> receiptsWithSize =  await DatabaseRepository.instance.getReceiptsBySize(parentId, lastOrder);
          items = [...foldersWithSize, ...receiptsWithSize];
          break;
        default:
          final List<Folder> folders = await DatabaseRepository.instance.getFoldersInFolderSortedBy(parentId, lastColumn, lastOrder);
          final List<Receipt> receipts = await DatabaseRepository.instance.getReceiptsInFolderSortedBy(parentId, lastColumn, lastOrder);
          items = [...folders, ...receipts];
          break;
      }

      // Remove the selected item from the list
      items.removeWhere((element) {
        if (element is Folder && selectedObject is Folder) {
          return element.id == selectedObject.id;
        } else if (element is Receipt && selectedObject is Receipt) {
          return element.id == selectedObject.id;
        }
        return false;
      });

      // Convert items to ListItems
      List<ListItem> listItems =
          items.map((item) => ListItem(item: item)).toList();

      emit(SelectMultipleActivated(
          items: listItems, initiallySelectedItem: selectedItem));
    } on Exception catch (e) {
      ...
    }
  }

现在在我的 UI 中,我有这个 BlocListener tp 填充一个可选项目列表:

@override
  Widget build(BuildContext context) {
    return BlocListener<SelectMultipleCubit, SelectMultipleState>(
      listener: (context, state) {
        if (state is SelectMultipleActivated) {
          // this code should only run once, when the SelectMultipleView is setup
          // currently SelectMultipleActivated is only emitted once so it is fine
          final initiallySelectedListItem = state.initiallySelectedItem;
          final restOfListItems = state.items;
          // adding initially selected item to currentlySelectedListItems
          currentlySelectedListItemsNotifier.value.add(initiallySelectedListItem);
          // adding initially selected item to allItems first
          allItems.add(initiallySelectedListItem);
          // adding rest of folder contents to allItems
          allItems.addAll(restOfListItems);
        }
        if (state is SelectMultipleActionSuccess) {
          Navigator.of(context).pop();
        }
      },

我发现使用print语句,该侦听器不会与修改后的版本一起运行,但它确实与activate()的原始版本一起运行activate()

我不知道为什么会这样,我也不明白为什么会这样。我希望侦听器使用原始和修改后的 activate() 方法运行。

谁能帮我确定为什么不是这种情况?

flutter bloc 状态管理 flutter-bloc

评论


答:

0赞 N.K. 11/15/2023 #1

问题摘要:
activate() 实际上不包含任何异步代码。添加 await 意味着 init() 调用后不会立即发出 SelectMultipleInitial、SelectMultipleLoading、SelectMultipleActivated,这意味着没有足够的时间调用 SelectMultipleView build() 方法,因此 BlocListener 尚未插入到小部件树中,导致监听器无法响应任何状态。

溶液: 将 BlocListener 移至 initState()

class _SelectMultipleViewState extends State<SelectMultipleView>

@override
  void initState() {
    super.initState();
    // only listening to state here since activate() is NOT async
    final selectMultipleCubitState = context.read<SelectMultipleCubit>().state;
    if (selectMultipleCubitState is SelectMultipleActivated) {
      // this code should only run once, when the SelectMultipleView is setup
      // currently SelectMultipleActivated is only emitted once so it is fine
      final initiallySelectedListItem =
          selectMultipleCubitState.initiallySelectedItem;
      final restOfListItems = selectMultipleCubitState.items;
      // adding initially selected item to currentlySelectedListItems
      currentlySelectedListItemsNotifier.value.add(initiallySelectedListItem);
      // adding initially selected item to allItems first
      allItems.add(initiallySelectedListItem);
      // adding rest of folder contents to allItems
      allItems.addAll(restOfListItems);
    }
  }