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