From 0d48bec7a24706e8f68153640b3e255509aa62e3 Mon Sep 17 00:00:00 2001 From: wen Date: Wed, 29 May 2024 01:26:48 +0800 Subject: [PATCH] Replace Stream with Future --- .../lib/cached_network_image.dart | 1 - .../lib/src/image_provider/_image_loader.dart | 1 - .../cached_network_image_provider.dart | 9 +- .../multi_image_stream_completer.dart | 238 ------------------ cached_network_image/pubspec.yaml | 2 +- 5 files changed, 5 insertions(+), 246 deletions(-) delete mode 100644 cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart diff --git a/cached_network_image/lib/cached_network_image.dart b/cached_network_image/lib/cached_network_image.dart index 99375aaa..317d8c87 100644 --- a/cached_network_image/lib/cached_network_image.dart +++ b/cached_network_image/lib/cached_network_image.dart @@ -7,4 +7,3 @@ export 'package:flutter_cache_manager/flutter_cache_manager.dart' export 'src/cached_image_widget.dart'; export 'src/image_provider/cached_network_image_provider.dart'; -export 'src/image_provider/multi_image_stream_completer.dart'; diff --git a/cached_network_image/lib/src/image_provider/_image_loader.dart b/cached_network_image/lib/src/image_provider/_image_loader.dart index a7304e07..54d714ce 100644 --- a/cached_network_image/lib/src/image_provider/_image_loader.dart +++ b/cached_network_image/lib/src/image_provider/_image_loader.dart @@ -108,7 +108,6 @@ class ImageLoader implements platform.ImageLoader { headers: headers, key: cacheKey, ); - await for (final result in stream) { if (result is DownloadProgress) { chunkEvents.add( diff --git a/cached_network_image/lib/src/image_provider/cached_network_image_provider.dart b/cached_network_image/lib/src/image_provider/cached_network_image_provider.dart index d853c535..08111b1c 100644 --- a/cached_network_image/lib/src/image_provider/cached_network_image_provider.dart +++ b/cached_network_image/lib/src/image_provider/cached_network_image_provider.dart @@ -1,7 +1,6 @@ import 'dart:async' show Future, StreamController; import 'dart:ui' as ui show Codec; -import 'package:cached_network_image/src/image_provider/multi_image_stream_completer.dart'; import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart' show ErrorListener, ImageRenderMethodForWeb; import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart' @@ -74,8 +73,8 @@ class CachedNetworkImageProvider DecoderBufferCallback decode, ) { final chunkEvents = StreamController(); - final imageStreamCompleter = MultiImageStreamCompleter( - codec: _loadBufferAsync(key, chunkEvents, decode), + final imageStreamCompleter = MultiFrameImageStreamCompleter( + codec: _loadBufferAsync(key, chunkEvents, decode).first, chunkEvents: chunkEvents.stream, scale: key.scale, informationCollector: () sync* { @@ -128,8 +127,8 @@ class CachedNetworkImageProvider ImageDecoderCallback decode, ) { final chunkEvents = StreamController(); - final imageStreamCompleter = MultiImageStreamCompleter( - codec: _loadImageAsync(key, chunkEvents, decode), + final imageStreamCompleter = MultiFrameImageStreamCompleter( + codec: _loadImageAsync(key, chunkEvents, decode).first, chunkEvents: chunkEvents.stream, scale: key.scale, informationCollector: () sync* { 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 deleted file mode 100644 index bc81795d..00000000 --- a/cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart +++ /dev/null @@ -1,238 +0,0 @@ -import 'dart:async'; -import 'dart:ui' as ui show Codec, FrameInfo; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/painting.dart'; -import 'package:flutter/scheduler.dart'; - -/// Slows down animations by this factor to help in development. -double get timeDilation => _timeDilation; -double _timeDilation = 1; - -/// An ImageStreamCompleter with support for loading multiple images. -class MultiImageStreamCompleter extends ImageStreamCompleter { - /// The constructor to create an MultiImageStreamCompleter. The [codec] - /// should be a stream with the images that should be shown. The - /// [chunkEvents] should indicate the [ImageChunkEvent]s of the first image - /// to show. - MultiImageStreamCompleter({ - required Stream codec, - required double scale, - Stream? chunkEvents, - InformationCollector? informationCollector, - }) : _informationCollector = informationCollector, - _scale = scale { - codec.listen( - (event) { - if (_timer != null) { - _nextImageCodec = event; - } else { - _handleCodecReady(event); - } - }, - onError: (Object error, StackTrace stack) { - reportError( - context: ErrorDescription('resolving an image codec'), - exception: error, - stack: stack, - informationCollector: informationCollector, - silent: true, - ); - }, - ); - if (chunkEvents != null) { - _chunkSubscription = chunkEvents.listen( - reportImageChunkEvent, - onError: (Object error, StackTrace stack) { - reportError( - context: ErrorDescription('loading an image'), - exception: error, - stack: stack, - informationCollector: informationCollector, - silent: true, - ); - }, - ); - } - } - - ui.Codec? _codec; - ui.Codec? _nextImageCodec; - final double _scale; - final InformationCollector? _informationCollector; - ui.FrameInfo? _nextFrame; - - // When the current was first shown. - Duration? _shownTimestamp; - - // The requested duration for the current frame; - Duration? _frameDuration; - - // How many frames have been emitted so far. - int _framesEmitted = 0; - Timer? _timer; - StreamSubscription? _chunkSubscription; - - // Used to guard against registering multiple _handleAppFrame callbacks for the same frame. - bool _frameCallbackScheduled = false; - - /// We must avoid disposing a completer if it never had a listener, even - /// if all [keepAlive] handles get disposed. - bool __hadAtLeastOneListener = false; - - bool __disposed = false; - - void _switchToNewCodec() { - _framesEmitted = 0; - _timer = null; - _handleCodecReady(_nextImageCodec!); - _nextImageCodec = null; - } - - void _handleCodecReady(ui.Codec codec) { - _codec = codec; - - if (hasListeners) { - _decodeNextFrameAndSchedule(); - } - } - - void _handleAppFrame(Duration timestamp) { - _frameCallbackScheduled = false; - if (!hasListeners) return; - if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) { - _emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale)); - _shownTimestamp = timestamp; - _frameDuration = _nextFrame!.duration; - _nextFrame = null; - if (_framesEmitted % _codec!.frameCount == 0 && _nextImageCodec != null) { - _switchToNewCodec(); - } else { - final completedCycles = _framesEmitted ~/ _codec!.frameCount; - if (_codec!.repetitionCount == -1 || - completedCycles <= _codec!.repetitionCount) { - _decodeNextFrameAndSchedule(); - } - } - return; - } - final delay = _frameDuration! - (timestamp - _shownTimestamp!); - _timer = Timer(delay * timeDilation, _scheduleAppFrame); - } - - bool _isFirstFrame() { - return _frameDuration == null; - } - - bool _hasFrameDurationPassed(Duration timestamp) { - return timestamp - _shownTimestamp! >= _frameDuration!; - } - - Future _decodeNextFrameAndSchedule() async { - try { - _nextFrame = await _codec!.getNextFrame(); - } on Object catch (exception, stack) { - reportError( - context: ErrorDescription('resolving an image frame'), - exception: exception, - stack: stack, - informationCollector: _informationCollector, - silent: true, - ); - 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(image: _nextFrame!.image, scale: _scale)); - return; - } - _scheduleAppFrame(); - } - - void _scheduleAppFrame() { - if (_frameCallbackScheduled) { - return; - } - _frameCallbackScheduled = true; - SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame); - } - - void _emitFrame(ImageInfo imageInfo) { - setImage(imageInfo); - _framesEmitted += 1; - } - - @override - void addListener(ImageStreamListener listener) { - __hadAtLeastOneListener = true; - if (!hasListeners && _codec != null) _decodeNextFrameAndSchedule(); - super.addListener(listener); - } - - @override - void removeListener(ImageStreamListener listener) { - super.removeListener(listener); - if (!hasListeners) { - _timer?.cancel(); - _timer = null; - __maybeDispose(); - } - } - - int __keepAliveHandles = 0; - - @override - ImageStreamCompleterHandle keepAlive() { - final delegateHandle = super.keepAlive(); - return _MultiImageStreamCompleterHandle(this, delegateHandle); - } - - void __maybeDispose() { - if (!__hadAtLeastOneListener || - __disposed || - hasListeners || - __keepAliveHandles != 0) { - return; - } - - __disposed = true; - - _chunkSubscription?.onData(null); - _chunkSubscription?.cancel(); - _chunkSubscription = null; - } -} - -class _MultiImageStreamCompleterHandle implements ImageStreamCompleterHandle { - _MultiImageStreamCompleterHandle(this._completer, this._delegateHandle) { - _completer!.__keepAliveHandles += 1; - } - - MultiImageStreamCompleter? _completer; - final ImageStreamCompleterHandle _delegateHandle; - - /// Call this method to signal the [ImageStreamCompleter] that it can now be - /// disposed when its last listener drops. - /// - /// This method must only be called once per object. - @override - void dispose() { - assert(_completer != null); - assert(_completer!.__keepAliveHandles > 0); - assert(!_completer!.__disposed); - - _delegateHandle.dispose(); - - _completer!.__keepAliveHandles -= 1; - _completer!.__maybeDispose(); - _completer = null; - } -} diff --git a/cached_network_image/pubspec.yaml b/cached_network_image/pubspec.yaml index ab9da65e..6b1d4c6c 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: