Image

作者: 不二客 | 来源:发表于2020-08-05 13:14 被阅读0次

    Image

    显示图像的小部件。

    const Image({
    Key key,
    @required this.image,// ImageProvider
    this.frameBuilder,
    this.loadingBuilder,
    this.errorBuilder,
    this.semanticLabel,
    this.excludeFromSemantics = false,
    this.width,//宽度
    this.height,//高度
    this.color,//背景颜色
    this.colorBlendMode, // 详见
    this.fit,// BoxFit fill contain cover fitWidth fitHeight none scaleDown
    this.alignment = Alignment.center, //位置
    this.repeat = ImageRepeat.noRepeat, //如何绘制未被图像覆盖的方框的任何部分。 repeat repeatX repeatY noRepeat
    this.centerSlice,
    this.matchTextDirection = false, //是否按[TextDirection]的方向绘制图像。
    this.gaplessPlayback = false,
    this.filterQuality = FilterQuality.low,
    })

    didChangeDependencies

    @override
      void didChangeDependencies() {
        _updateInvertColors();
        _resolveImage(); //
    
        if (TickerMode.of(context))
          _listenToStream();
        else
          _stopListeningToStream();
    
        super.didChangeDependencies();
      }
    

    _resolveImage

      void _resolveImage() {
        final ScrollAwareImageProvider provider = ScrollAwareImageProvider<dynamic>(
          context: _scrollAwareContext,
          imageProvider: widget.image,
        );
        final ImageStream newStream =
          provider.resolve(createLocalImageConfiguration(
            context,
            size: widget.width != null && widget.height != null ? Size(widget.width, widget.height) : null,
          ));
        _updateSourceStream(newStream);
      }
    

    createLocalImageConfiguration

    ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size size }) {
      return ImageConfiguration(
        bundle: DefaultAssetBundle.of(context),
        devicePixelRatio: MediaQuery.of(context, nullOk: true)?.devicePixelRatio ?? 1.0,
        locale: Localizations.localeOf(context, nullOk: true),
        textDirection: Directionality.of(context),
        size: size,
        platform: defaultTargetPlatform,
      );
    }
    

    ImageConfiguration

     const ImageConfiguration({
        this.bundle,
        this.devicePixelRatio,
        this.locale,
        this.textDirection,
        this.size,
        this.platform,
      });
    

    provider.resolve configuration 转化成 图片流

     @nonVirtual
      ImageStream resolve(ImageConfiguration configuration) {
        assert(configuration != null);
        final ImageStream stream = createStream(configuration);
        _createErrorHandlerAndKey(
          configuration,
          (T key, ImageErrorListener errorHandler) {
            resolveStreamForKey(configuration, stream, key, errorHandler);
          },
          (T key, dynamic exception, StackTrace stack) async {
            await null; // wait an event turn in case a listener has been added to the image stream.
            final _ErrorImageCompleter imageCompleter = _ErrorImageCompleter();
            stream.setCompleter(imageCompleter);
            InformationCollector collector;
            assert(() {
              collector = () sync* {
                yield DiagnosticsProperty<ImageProvider>('Image provider', this);
                yield DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration);
                yield DiagnosticsProperty<T>('Image key', key, defaultValue: null);
              };
              return true;
            }());
            imageCompleter.setError(
              exception: exception,
              stack: stack,
              context: ErrorDescription('while resolving an image'),
              silent: true, // could be a network error or whatnot
              informationCollector: collector
              );
            },
          );
        return stream;
      }
    
      @protected
      ImageStream createStream(ImageConfiguration configuration) {
        return ImageStream();
      }
    
     void _createErrorHandlerAndKey(
        ImageConfiguration configuration,
        _KeyAndErrorHandlerCallback<T> successCallback,
        _AsyncKeyErrorHandler<T> errorCallback,
      ) {
        T obtainedKey;
        bool didError = false;
        Future<void> handleError(dynamic exception, StackTrace stack) async {
          if (didError) {
            return;
          }
          if (!didError) {
            errorCallback(obtainedKey, exception, stack);
          }
          didError = true;
        }
    
    @protected
      void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, T key, ImageErrorListener handleError) {
        // This is an unusual edge case where someone has told us that they found
        // the image we want before getting to this method. We should avoid calling
        // load again, but still update the image cache with LRU information.
        if (stream.completer != null) {
          final ImageStreamCompleter completer = PaintingBinding.instance.imageCache.putIfAbsent(
            key,
            () => stream.completer,
            onError: handleError,
          );
          assert(identical(completer, stream.completer));
          return;
        }
        final ImageStreamCompleter completer = PaintingBinding.instance.imageCache.putIfAbsent(
          key,
          () => load(key, PaintingBinding.instance.instantiateImageCodec),
          onError: handleError,
        );
        if (completer != null) {
          stream.setCompleter(completer);
        }
      }
    
     @protected
      void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, T key, ImageErrorListener handleError) {
        // This is an unusual edge case where someone has told us that they found
        // the image we want before getting to this method. We should avoid calling
        // load again, but still update the image cache with LRU information.
        if (stream.completer != null) {
          final ImageStreamCompleter completer = PaintingBinding.instance.imageCache.putIfAbsent(
            key,
            () => stream.completer,
            onError: handleError,
          );
          assert(identical(completer, stream.completer));
          return;
        }
        final ImageStreamCompleter completer = PaintingBinding.instance.imageCache.putIfAbsent(
          key,
          () => load(key, PaintingBinding.instance.instantiateImageCodec),
          onError: handleError,
        );
        if (completer != null) {
          stream.setCompleter(completer);
        }
      }
    
    ImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener onError }) {
        assert(key != null);
        assert(loader != null);
        TimelineTask timelineTask;
        TimelineTask listenerTask;
        if (!kReleaseMode) {
          timelineTask = TimelineTask()..start(
            'ImageCache.putIfAbsent',
            arguments: <String, dynamic>{
              'key': key.toString(),
            },
          );
        }
        ImageStreamCompleter result = _pendingImages[key]?.completer;
        // Nothing needs to be done because the image hasn't loaded yet.
        if (result != null) {
          if (!kReleaseMode) {
            timelineTask.finish(arguments: <String, dynamic>{'result': 'pending'});
          }
          return result;
        }
        // Remove the provider from the list so that we can move it to the
        // recently used position below.
        // Don't use _touch here, which would trigger a check on cache size that is
        // not needed since this is just moving an existing cache entry to the head.
        final _CachedImage image = _cache.remove(key);
        if (image != null) {
          if (!kReleaseMode) {
            timelineTask.finish(arguments: <String, dynamic>{'result': 'keepAlive'});
          }
          // The image might have been keptAlive but had no listeners (so not live).
          // Make sure the cache starts tracking it as live again.
          _trackLiveImage(key, _LiveImage(image.completer, image.sizeBytes, () => _liveImages.remove(key)));
          _cache[key] = image;
          return image.completer;
        }
    
        final _CachedImage liveImage = _liveImages[key];
        if (liveImage != null) {
          _touch(key, liveImage, timelineTask);
          if (!kReleaseMode) {
            timelineTask.finish(arguments: <String, dynamic>{'result': 'keepAlive'});
          }
          return liveImage.completer;
        }
    
        try {
          result = loader();
          _trackLiveImage(key, _LiveImage(result, null, () => _liveImages.remove(key)));
        } catch (error, stackTrace) {
          if (!kReleaseMode) {
            timelineTask.finish(arguments: <String, dynamic>{
              'result': 'error',
              'error': error.toString(),
              'stackTrace': stackTrace.toString(),
            });
          }
          if (onError != null) {
            onError(error, stackTrace);
            return null;
          } else {
            rethrow;
          }
        }
    
        if (!kReleaseMode) {
          listenerTask = TimelineTask(parent: timelineTask)..start('listener');
        }
        // If we're doing tracing, we need to make sure that we don't try to finish
        // the trace entry multiple times if we get re-entrant calls from a multi-
        // frame provider here.
        bool listenedOnce = false;
    
        // We shouldn't use the _pendingImages map if the cache is disabled, but we
        // will have to listen to the image at least once so we don't leak it in
        // the live image tracking.
        // If the cache is disabled, this variable will be set.
        _PendingImage untrackedPendingImage;
        void listener(ImageInfo info, bool syncCall) {
          // Images that fail to load don't contribute to cache size.
          final int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;
    
          final _CachedImage image = _CachedImage(result, imageSize);
    
          _trackLiveImage(
            key,
            _LiveImage(
              result,
              imageSize,
              () => _liveImages.remove(key),
            ),
            // This should result in a put if `loader()` above executed
            // synchronously, in which case syncCall is true and we arrived here
            // before we got a chance to track the image otherwise.
            debugPutOk: syncCall,
          );
    
          final _PendingImage pendingImage = untrackedPendingImage ?? _pendingImages.remove(key);
          if (pendingImage != null) {
            pendingImage.removeListener();
          }
          // Only touch if the cache was enabled when resolve was initially called.
          if (untrackedPendingImage == null) {
            _touch(key, image, listenerTask);
          }
    
          if (!kReleaseMode && !listenedOnce) {
            listenerTask.finish(arguments: <String, dynamic>{
              'syncCall': syncCall,
              'sizeInBytes': imageSize,
            });
            timelineTask.finish(arguments: <String, dynamic>{
              'currentSizeBytes': currentSizeBytes,
              'currentSize': currentSize,
            });
          }
          listenedOnce = true;
        }
    
        final ImageStreamListener streamListener = ImageStreamListener(listener);
        if (maximumSize > 0 && maximumSizeBytes > 0) {
          _pendingImages[key] = _PendingImage(result, streamListener);
        } else {
          untrackedPendingImage = _PendingImage(result, streamListener);
        }
        // Listener is removed in [_PendingImage.removeListener].
        result.addListener(streamListener);
    
        return result;
      }
    
    

    ImageStream

    图像资源的句柄。

    ImageStreamCompleter

    用于管理[飞镖:ui.Image][ImageStream]s的对象。

    相关文章

      网友评论

          本文标题:Image

          本文链接:https://www.haomeiwen.com/subject/btkslktx.html