Flutter中PaginationNotifier dispose后使用报错的解决问询
问题场景
使用StateNotifierProvider.autoDispose实现图片分页加载,通过滚动控制器监听滚动位置触发fetchNextBatch()加载更多。但当异步请求执行过程中调用navigator.pop()返回上一页时,会抛出以下错误:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: Tried to use PaginationNotifier after
disposewas called.
Consider checkingmounted.
原因是页面出栈后,autoDispose的Provider会立即销毁PaginationNotifier,但未完成的异步请求返回后仍尝试更新已销毁的状态。
解决方案
1. 在异步操作完成后检查mounted状态
在所有会更新state的异步流程中,每次await之后都先检查mounted状态,若已销毁则直接返回,避免执行状态更新操作。
修改后的PaginationNotifier关键代码:
Future<void> _fetchFirstBatch() async { try { _numberOfItemsInFolder = await (await PhotoManager.getAssetPathList()).firstWhere((element) => element.id == folderId).assetCountAsync; // 检查是否已销毁 if (!mounted) return; state = const AsyncValue.loading(); final List<Uint8List?> result = await _fetchNextItems(); if (!mounted) return; _updateData(result); } catch (e, stk) { if (!mounted) return; state = AsyncValue.error(e, stk); } } Future<void> fetchNextBatch() async { if (_alreadyFetching || !_hasMoreItems) return; _alreadyFetching = true; // 先检查是否已销毁 if (!mounted) { _alreadyFetching = false; return; } log("data updated"); state = AsyncValue.data(_items); try { final result = await _fetchNextItems(); if (!mounted) return; _updateData(result); } catch (e, stk) { if (!mounted) return; state = AsyncValue.error(e, stk); log("error catched"); } _alreadyFetching = false; } void _updateData(List<Uint8List?> result) { if (!mounted) return; if (result.isEmpty) { state = AsyncValue.data(_items); } else { state = AsyncValue.data(_items..addAll(result)); } _hasMoreItems = _numberOfItemsInFolder > _items.length; }
2. 确保滚动监听中的mounted检查有效
你的滚动监听代码已经加入了ref.read(paginationImagesProvider.notifier).mounted的判断,这部分可以保留,确保只有在Notifier未销毁时才触发加载请求:
controller.addListener(() { double maxScroll = controller.position.maxScrollExtent; double position = controller.position.pixels; if ((position > maxScroll * 0.2 || position == 0) && ref.read(paginationImagesProvider.notifier).mounted) { ref.read(paginationImagesProvider.notifier).fetchNextBatch(); } });
3. 可选:取消未完成的异步请求(进阶优化)
如果需要彻底终止未完成的异步操作,可以使用CancelableOperation封装图片加载请求,在StateNotifier的dispose方法中取消操作:
class PaginationNotifier extends StateNotifier<AsyncValue<List<Uint8List?>>> { // 新增CancelableOperation用于管理异步请求 CancelableOperation? _currentFetchOperation; // ... 其他成员变量和构造函数 @override void dispose() { _currentFetchOperation?.cancel(); super.dispose(); } Future<List<Uint8List?>> _fetchNextItems() async { // 用CancelableOperation封装请求 _currentFetchOperation = CancelableOperation.fromFuture( Future(() async { List<AssetEntity> images = (await (await PhotoManager.getAssetPathList()) .firstWhere((element) => element.id == folderId) .getAssetListRange(start: _items.length, end: _items.length + itemsPerBatch)); List<Uint8List?> newItems = []; for (AssetEntity image in images) { newItems.add(await image.thumbnailData); } return newItems; }) ); try { return await _currentFetchOperation!.value; } on OperationCanceledError { // 请求被取消时返回空列表或处理逻辑 return []; } finally { _currentFetchOperation = null; } } }
这个方案可以避免不必要的资源消耗,同时彻底杜绝异步请求完成后更新状态的可能。
内容的提问来源于stack exchange,提问作者PazKiller

