I sometime got the following error in my Crashlytics
:
Bad state: Future already completed. Error thrown by an image listener.
This is my function to calculate an image dimension, where I think the error is thrown:
Future<Size> calculateImageDimension(
String imageUrl, BuildContext context) async {
Completer<Size> completer = Completer();
late Image image;
image = Image.network(
imageUrl,
);
if (!await hasValidImageData(imageUrl, context)) {
// ignore: avoid_print
print('invaaaaliiiiiiiiiiiiiiddddd');
return Future.value(const Size(0, 0));
}
image.image.resolve(const ImageConfiguration()).addListener(
ImageStreamListener(
(ImageInfo image, bool synchronousCall) {
var myImage = image.image;
Size size = Size(myImage.width.toDouble(), myImage.height.toDouble());
if (!completer.isCompleted) {
completer.complete(size);
}
},
),
);
return completer.future;
}
What am I doing wrong here? I couldn’t reproduce the error on my side… Let me know if you need any more info.
2
Answers
Just a random thought, have you tried to replace
with something like
My guess is that at some point (at the start probably), there’s no valid image data and thus it returns a completed
Future
. Another option is that there’s a race condition somewhere but without more information it’d be hard to point you towards the right direction.I think your
Future
is trying to completed more than once. I mean theFuture
returning by yourcalculateImageDimension
is trying to return and complete again and again so that’s probably create the error.After checking your code, I see here that
Future
is inside your callback(ImageStreamListener
) and called every time if the image is being loaded from the network and/or the network conditions change until the image is resolve.So, let me change the structure of your code like below;
See that the
ImageStreamListener
is added to theImageStream
returned by the resolve method of theImageProvider
. This because we need to ensure that yourFuture
is only completed once when the image is loaded for the first time. For this, even if the image will loaded multiple times, it will not affect theFuture
and not be completed again. This approach hopingly to prevent error(Bad state: Future already completed.)Lastly, when your done, do call
removeListener
method on theImageStream
to remove the listener by astream.removeListener(listener);
. This should be done in thedispose
method of your widget, or wherever you’re done with theFuture
returned bycalculateImageDimension
.This seems to be unnecessary, but we use it to prevent memory leaks.