BlocListener未更新排查:修改activate()后监听失效问题
BLoC监听失效问题:从缓存取数据后BlocListener不触发
问题描述
修改SelectMultipleCubit的activate()方法后,UI中的BlocListener不再响应SelectMultipleActivated状态。原版本从数据库异步获取数据,监听正常;修改后改为从folderViewCubit的缓存cachedCurrentlyDisplayedFiles同步取数据,即便activate()已emit目标状态,监听代码仍不执行。
修改后的activate()方法
void activate(ListItem selectedItem) async { emit(SelectMultipleLoading()); try { // 获取选中项的父ID和类型 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); // 移除选中项 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}'); // 转换为ListItem列表 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 { // 获取选中项的父ID和类型 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; } // 获取排序配置 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; } // 移除选中项 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; }); // 转换为ListItem列表 List<ListItem> listItems = items.map((item) => ListItem(item: item)).toList(); emit(SelectMultipleActivated( items: listItems, initiallySelectedItem: selectedItem)); } on Exception catch (e) { // 异常处理逻辑 emit(SelectMultipleError()); } }
UI中的BlocListener代码
@override Widget build(BuildContext context) { return BlocListener<SelectMultipleCubit, SelectMultipleState>( listener: (context, state) { if (state is SelectMultipleActivated) { // 仅在SelectMultipleView初始化时执行一次 final initiallySelectedListItem = state.initiallySelectedItem; final restOfListItems = state.items; // 添加选中项到当前选中列表 currentlySelectedListItemsNotifier.value.add(initiallySelectedListItem); // 添加选中项到全量列表 allItems.add(initiallySelectedListItem); // 添加剩余内容到全量列表 allItems.addAll(restOfListItems); } if (state is SelectMultipleActionSuccess) { Navigator.of(context).pop(); } }, // 其他Widget代码... ); }
问题原因及解决方案
核心原因
原版本中的异步数据库操作会让事件循环有时间处理状态更新,确保BlocListener能接收到每一次状态变化。而修改后的版本全为同步逻辑,emit(SelectMultipleLoading())后立刻同步执行完所有代码并emitSelectMultipleActivated,两个状态更新处于同一个事件循环周期,BlocListener可能只处理最后一个状态,或因状态更新过快跳过监听触发。
另外需排查:
- 状态相等性:若
SelectMultipleActivated未正确重写==和hashCode,BLoC会认为新状态与旧状态相同,不触发监听。 - 缓存数据有效性:确认
cachedCurrentlyDisplayedFiles的元素类型、内容与原数据库返回一致,避免removeWhere逻辑失效导致状态异常。
解决方案
拆分事件循环周期:在同步逻辑中加入微任务,让最终状态的emit进入下一个事件循环,确保前一个状态被处理:
// 在emit SelectMultipleActivated前添加 await Future.microtask(() {}); emit(SelectMultipleActivated( items: listItems, initiallySelectedItem: selectedItem));正确实现状态的相等性判断:
import 'package:flutter/foundation.dart'; class SelectMultipleActivated extends SelectMultipleState { final List<ListItem> items; final ListItem initiallySelectedItem; SelectMultipleActivated({required this.items, required this.initiallySelectedItem}); @override bool operator ==(Object other) => identical(this, other) || other is SelectMultipleActivated && runtimeType == other.runtimeType && listEquals(items, other.items) && initiallySelectedItem == other.initiallySelectedItem; @override int get hashCode => items.hashCode ^ initiallySelectedItem.hashCode; }验证缓存数据:打印
cachedCurrentlyDisplayedFiles内容,确认removeWhere逻辑正确执行,生成的listItems符合预期。
内容的提问来源于stack exchange,提问作者N.K.
相关产品推荐
相关产品推荐

