You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中Crashlytics报Bad state: Future already completed错误排查

问题分析与修复方案

错误根源

Bad state: Future already completed 错误本质是同一个Completer被多次调用complete方法。你代码里的!completer.isCompleted判断无法完全避免这个问题:

  • 图片流的ImageStreamListener回调可能因缓存复用、图片重新加载等场景被多次触发,即便第一次已完成completer,后续回调仍可能尝试执行complete
  • 未处理图片加载失败的场景,若图片加载出错,completer会一直处于未完成状态,同时错误回调若未正确处理,也可能间接引发重复完成的问题

代码修复要点

1. 回调触发后立即移除Listener

在ImageStreamListener的回调中,完成completer后立刻移除当前Listener,确保回调只执行一次:

final imageStream = image.image.resolve(const ImageConfiguration());
late ImageStreamListener listener;

listener = ImageStreamListener(
  (ImageInfo imageInfo, bool synchronousCall) {
    final myImage = imageInfo.image;
    final size = Size(myImage.width.toDouble(), myImage.height.toDouble());
    if (!completer.isCompleted) {
      completer.complete(size);
    }
    // 移除Listener,阻止后续重复触发
    imageStream.removeListener(listener);
  },
  // 新增错误处理,避免completer永久挂起
  onError: (Object error, StackTrace? stackTrace) {
    if (!completer.isCompleted) {
      completer.completeError(error, stackTrace);
    }
    imageStream.removeListener(listener);
  },
);

imageStream.addListener(listener);

2. 统一Future返回逻辑

原代码中hasValidImageData返回false时直接return Future.value(...),虽不会直接引发错误,但为了逻辑一致性,建议改用completer完成:

if (!await hasValidImageData(imageUrl, context)) {
  print('invalid image');
  completer.complete(const Size(0, 0));
  return completer.future;
}

3. 完整修复后的代码

Future<Size> calculateImageDimension(
    String imageUrl, BuildContext context) async {
  Completer<Size> completer = Completer();
  final image = Image.network(imageUrl);

  if (!await hasValidImageData(imageUrl, context)) {
    print('invalid image');
    completer.complete(const Size(0, 0));
    return completer.future;
  }

  final imageStream = image.image.resolve(const ImageConfiguration());
  late ImageStreamListener listener;
  
  listener = ImageStreamListener(
    (ImageInfo imageInfo, bool synchronousCall) {
      final myImage = imageInfo.image;
      final size = Size(myImage.width.toDouble(), myImage.height.toDouble());
      if (!completer.isCompleted) {
        completer.complete(size);
      }
      imageStream.removeListener(listener);
    },
    onError: (Object error, StackTrace? stackTrace) {
      if (!completer.isCompleted) {
        completer.completeError(error, stackTrace);
      }
      imageStream.removeListener(listener);
    },
  );

  imageStream.addListener(listener);

  return completer.future;
}

额外优化建议

  • 若hasValidImageData是检查图片可访问性,建议合并到图片加载的错误处理中,避免重复发起网络请求
  • 也可以尝试使用dart:ui的decodeImageFromList方法:先通过网络请求获取图片字节,再解析尺寸,这种方式更可控,无需依赖Image.network的流回调

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 22:05:15