diff --git a/packages/flutter/lib/src/painting/image_stream.dart b/packages/flutter/lib/src/painting/image_stream.dart index abffeee7c8..1b6a8a5e37 100644 --- a/packages/flutter/lib/src/painting/image_stream.dart +++ b/packages/flutter/lib/src/painting/image_stream.dart @@ -920,6 +920,12 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { return; } if (_codec!.frameCount == 1) { + // ImageStreamCompleter listeners removed while waiting for next frame to + // be decoded. + // There's no reason to emit the frame without active listeners. + if (!hasListeners) { + return; + } // This is not an animated image, just return it and don't schedule more // frames. _emitFrame(ImageInfo( diff --git a/packages/flutter/test/painting/image_stream_test.dart b/packages/flutter/test/painting/image_stream_test.dart index 491b115cf8..6b984622ca 100644 --- a/packages/flutter/test/painting/image_stream_test.dart +++ b/packages/flutter/test/painting/image_stream_test.dart @@ -130,6 +130,31 @@ void main() { expect(mockCodec.numFramesAsked, 1); }); + testWidgets('Decoding does not crash when disposed', (WidgetTester tester) async { + final Completer completer = Completer(); + final MockCodec mockCodec = MockCodec(); + mockCodec.frameCount = 1; + final ImageStreamCompleter imageStream = MultiFrameImageStreamCompleter( + codec: completer.future, + scale: 1.0, + ); + + completer.complete(mockCodec); + await tester.idle(); + expect(mockCodec.numFramesAsked, 0); + + final ImageListener listener = (ImageInfo image, bool synchronousCall) { }; + final ImageStreamListener streamListener = ImageStreamListener(listener); + imageStream.addListener(streamListener); + await tester.idle(); + expect(mockCodec.numFramesAsked, 1); + + final FrameInfo frame = FakeFrameInfo(const Duration(milliseconds: 200), image20x10); + mockCodec.completeNextFrame(frame); + imageStream.removeListener(streamListener); + await tester.idle(); + }); + testWidgets('Chunk events of base ImageStreamCompleter are delivered', (WidgetTester tester) async { final List chunkEvents = []; final StreamController streamController = StreamController();