diff --git a/cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart b/cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart index bc81795d..76f6a806 100644 --- a/cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart +++ b/cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart @@ -18,6 +18,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { MultiImageStreamCompleter({ required Stream codec, required double scale, + String? debugLabel, Stream? chunkEvents, InformationCollector? informationCollector, }) : _informationCollector = informationCollector, @@ -63,7 +64,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { ui.FrameInfo? _nextFrame; // When the current was first shown. - Duration? _shownTimestamp; + late Duration _shownTimestamp; // The requested duration for the current frame; Duration? _frameDuration; @@ -99,16 +100,25 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { void _handleAppFrame(Duration timestamp) { _frameCallbackScheduled = false; - if (!hasListeners) return; + if (!hasListeners) { + return; + } + assert(_nextFrame != null); if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) { - _emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale)); + _emitFrame(ImageInfo( + image: _nextFrame!.image.clone(), + scale: _scale, + debugLabel: debugLabel, + )); _shownTimestamp = timestamp; _frameDuration = _nextFrame!.duration; + _nextFrame!.image.dispose(); _nextFrame = null; + if (_framesEmitted % _codec!.frameCount == 0 && _nextImageCodec != null) { _switchToNewCodec(); } else { - final completedCycles = _framesEmitted ~/ _codec!.frameCount; + final int completedCycles = _framesEmitted ~/ _codec!.frameCount; if (_codec!.repetitionCount == -1 || completedCycles <= _codec!.repetitionCount) { _decodeNextFrameAndSchedule(); @@ -116,8 +126,10 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { } return; } - final delay = _frameDuration! - (timestamp - _shownTimestamp!); - _timer = Timer(delay * timeDilation, _scheduleAppFrame); + final Duration delay = _frameDuration! - (timestamp - _shownTimestamp); + _timer = Timer(delay * timeDilation, () { + _scheduleAppFrame(); + }); } bool _isFirstFrame() { @@ -129,9 +141,13 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { } Future _decodeNextFrameAndSchedule() async { + // This will be null if we gave it away. If not, it's still ours and it + // must be disposed of. + _nextFrame?.image.dispose(); + _nextFrame = null; try { _nextFrame = await _codec!.getNextFrame(); - } on Object catch (exception, stack) { + } catch (exception, stack) { reportError( context: ErrorDescription('resolving an image frame'), exception: exception, @@ -148,10 +164,15 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { if (!hasListeners) { return; } - // This is not an animated image, just return it and don't schedule more // frames. - _emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale)); + _emitFrame(ImageInfo( + image: _nextFrame!.image.clone(), + scale: _scale, + debugLabel: debugLabel, + )); + _nextFrame!.image.dispose(); + _nextFrame = null; return; } _scheduleAppFrame(); @@ -173,7 +194,12 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { @override void addListener(ImageStreamListener listener) { __hadAtLeastOneListener = true; - if (!hasListeners && _codec != null) _decodeNextFrameAndSchedule(); + if (!hasListeners && + _codec != null && + ( //_currentImage == null || + _codec!.frameCount > 1)) { + _decodeNextFrameAndSchedule(); + } super.addListener(listener); } diff --git a/cached_network_image/pubspec.yaml b/cached_network_image/pubspec.yaml index d00f051f..db25dca9 100644 --- a/cached_network_image/pubspec.yaml +++ b/cached_network_image/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: cached_network_image_web: ^1.1.1 flutter: sdk: flutter - flutter_cache_manager: ^3.3.1 + flutter_cache_manager: ^3.3.2 octo_image: ^2.0.0 dev_dependencies: