Image
/// The [Image.asset], [Image.network], [Image.file], and [Image.memory]
/// constructors allow a custom decode size to be specified through
/// `cacheWidth` and `cacheHeight` parameters. The engine will decode the
/// image to the specified size, which is primarily intended to reduce the
/// memory usage of [ImageCache].
这段注释解释了cacheWidth和cacheHeight参数的作用,当这些参数被指定时,引擎将解码图像到指定的大小,主要是为了减少[ImageCache]的内存使用。但在Web平台上使用网络图像时,这些参数将被忽略,因为Web引擎将网络图像的解码委托给Web,不支持自定义解码大小。
[Image.network]
/// If [cacheWidth] or [cacheHeight] are provided, it indicates to the
/// engine that the image should be decoded at the specified size. The image
/// will be rendered to the constraints of the layout or [width] and [height]
/// regardless of these parameters. These parameters are primarily intended
/// to reduce the memory usage of [ImageCache].
这段注释描述了在使用Image.network()时,如果提供了cacheWidth或cacheHeight参数,它们表示引擎应该在指定的大小上解码图像。但是,无论这些参数如何,图像都会按照布局的约束或width和height属性进行渲染。这些参数主要是为了减少ImageCache的内存使用。
疑惑解答
-
cacheWidth 、cacheHeight怎样设置更合理?
-
cacheWidth和cacheHeight是在创建ImageProvider实例时可以传递的可选参数,用于指定将图像解码为指定大小,以便在存储图像时减少内存占用。但是,这些参数不会影响实际呈现的图像大小。图像的呈现大小由布局约束、width和height参数确定。 -
如果您知道要呈现的图像大小,最好将
width和height参数设置为具体值,这将确保在图像加载时不会出现不美观的布局更改。如果没有指定width和height参数,则应该在将Image小部件放置在任何父部件中之前,确保其处于具有紧密布局约束的上下文中。这将确保在图像加载时布局大小不会发生变化。 -
设置
cacheWidth和cacheHeight的值,需要考虑到两个方面:显示的图像大小和内存消耗。 -
一般情况下,如果要显示一个小尺寸的图像,可以将
cacheWidth和cacheHeight设置为这个尺寸的值。这样可以减小内存消耗,因为在解码过程中会使用更少的内存。如果要显示一个大尺寸的图像,可以将这些参数设置为较小的值,以尽量减小内存占用。但是,如果将这些参数设置得太小,图像质量可能会受到影响。 -
总之,设置这些参数的值需要在内存占用和图像质量之间做出平衡。可以根据具体情况进行调整。
UML图
_ImageState
didChangeDependencies
@override
void didChangeDependencies() {
_updateInvertColors();
_resolveImage();
if (TickerMode.of(context)) {
_listenToStream();
} else {
_stopListeningToStream(keepStreamAlive: true);
}
super.didChangeDependencies();
}
didChangeDependencies 是一个重要的生命周期方法,它在组件的依赖关系发生变化时被调用。在 _ImageState 中,该方法首先调用 _updateInvertColors 方法,用于更新图片颜色的反转状态。接着调用 _resolveImage 方法,该方法会解析图片并设置 _imageStream 的监听器,以便在图片加载完成后更新 _imageInfo 状态。
然后,该方法通过 TickerMode 判断当前组件是否处于 Ticker 模式,如果是,则调用 _listenToStream 方法,否则调用 _stopListeningToStream 方法并将 keepStreamAlive 参数设置为 true,以保持 _imageStream 的活性状态。
最后,该方法调用父类的 didChangeDependencies 方法,以便执行其他依赖关系变化的操作。
_resolveImage
void _resolveImage() {
final ScrollAwareImageProvider provider = ScrollAwareImageProvider<Object>(
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,
));
assert(newStream != null);
_updateSourceStream(newStream);
}
这段代码定义了一个私有方法 _resolveImage(),其主要功能是将给定的 widget.image 解析为一个 ImageStream 并更新状态。具体来说:
- 创建了一个
ScrollAwareImageProvider实例provider,该实例封装了widget.image和_scrollAwareContext。 - 调用
provider的resolve方法,并传入一个createLocalImageConfiguration方法创建的本地图片配置对象。 - 将返回的
ImageStream更新到状态中。
这里的 ScrollAwareImageProvider 是 ImageProvider 的一个子类,主要作用是支持图片懒加载和滚动优化。createLocalImageConfiguration 方法返回一个包含了 BuildContext 和 Size 的图片配置对象,它会被传递给图片解码器,以确保正确地解码图片。
_updateSourceStream
// Updates _imageStream to newStream, and moves the stream listener
// registration from the old stream to the new stream (if a listener was
// registered).
void _updateSourceStream(ImageStream newStream) {
if (_imageStream?.key == newStream.key) {
return;
}
if (_isListeningToStream) {
_imageStream!.removeListener(_getListener());
}
if (!widget.gaplessPlayback) {
setState(() { _replaceImage(info: null); });
}
setState(() {
_loadingProgress = null;
_frameNumber = null;
_wasSynchronouslyLoaded = false;
});
_imageStream = newStream;
if (_isListeningToStream) {
_imageStream!.addListener(_getListener());
}
}
该方法用于更新 Image 控件的图像数据流 _imageStream,并将旧的数据流的监听器移动到新的数据流(如果有监听器注册)。如果 gaplessPlayback 为 false,则会调用 _replaceImage 方法,将 _imageInfo 置为 null。同时,还会重置加载进度、帧编号以及同步加载标志等状态。
具体来说,该方法首先比较旧的数据流和新数据流的关键(key)是否相同,如果相同则直接返回。否则,如果有监听器注册,则将旧的数据流的监听器移除;然后重置状态,包括加载进度、帧编号和同步加载标志等;最后将 _imageStream 更新为新的数据流,并将监听器注册到新的数据流上。
_imageStreamListener
ImageStreamListener _getListener({bool recreateListener = false}) {
if(_imageStreamListener == null || recreateListener) {
_lastException = null;
_lastStack = null;
_imageStreamListener = ImageStreamListener(
_handleImageFrame,
onChunk: widget.loadingBuilder == null ? null : _handleImageChunk,
onError: widget.errorBuilder != null || kDebugMode
? (Object error, StackTrace? stackTrace) {
setState(() {
_lastException = error;
_lastStack = stackTrace;
});
assert(() {
if (widget.errorBuilder == null) {
// ignore: only_throw_errors, since we're just proxying the error.
throw error; // Ensures the error message is printed to the console.
}
return true;
}());
}
: null,
);
}
return _imageStreamListener!;
}
_getListener方法返回一个ImageStreamListener对象,用于处理图片流的事件。如果recreateListener为true,则会重新创建一个新的ImageStreamListener对象。在创建ImageStreamListener对象时,如果loadingBuilder不为null,则将通过onChunk参数注册回调函数_handleImageChunk来处理图片流加载的进度。如果errorBuilder不为null或者是开启了调试模式,那么将通过onError参数注册回调函数来处理图片流加载失败的情况,其中将会通过setState方法更新错误信息。如果errorBuilder为null,则会将错误信息直接抛出以确保错误信息被输出到控制台。
_handleImageFrame
void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) {
setState(() {
_replaceImage(info: imageInfo);
_loadingProgress = null;
_lastException = null;
_lastStack = null;
_frameNumber = _frameNumber == null ? 0 : _frameNumber! + 1;
_wasSynchronouslyLoaded = _wasSynchronouslyLoaded | synchronousCall;
});
}
_handleImageFrame方法在ImageStream流中有可用的图像帧时被调用。它接收ImageInfo对象和一个布尔值synchronousCall作为参数,表示图像帧是否已同步加载。
在这个方法中,它通过调用setState方法来更新当前_ImageState对象的状态。具体来说,它通过调用_replaceImage方法将ImageInfo对象传递给_imageInfo字段,从而将图像帧显示在界面上。同时,它将_loadingProgress字段设置为null,表示图像加载进度为空;将_lastException和_lastStack字段设置为null,表示没有错误发生;将_frameNumber字段设置为当前帧的编号,如果之前没有帧,则将其设置为0;将_wasSynchronouslyLoaded字段设置为synchronousCall的值,表示图像是否同步加载。
_replaceImage
_imageInfo?.dispose();
_imageInfo = info;
}
_replaceImage 方法用于替换当前的图片,传入参数为 ImageInfo 对象。在方法内部,首先会调用 _imageInfo 的 dispose 方法来释放之前的图片资源,然后将新的 ImageInfo 对象赋值给 _imageInfo。
_handleImageChunk
```
void _handleImageChunk(ImageChunkEvent event) {
assert(widget.loadingBuilder != null);
setState(() {
_loadingProgress = event;
_lastException = null;
_lastStack = null;
});
}
```
该方法用于处理图片加载进度事件。当使用自定义loadingBuilder时,可以在该方法中更新进度条等加载UI,以反映图片加载进度。在方法内部,通过调用setState方法来更新组件的状态,包括_loadingProgress,_lastException和_lastStack属性。其中,_loadingProgress属性是一个ImageChunkEvent对象,表示图片加载进度信息。_lastException和_lastStack属性则用于记录最近一次加载图片时出现的异常和调用栈信息,方便调试。
_stopListeningToStream
/// Stops listening to the image stream, if this state object has attached a
/// listener.
///
/// If the listener from this state is the last listener on the stream, the
/// stream will be disposed. To keep the stream alive, set `keepStreamAlive`
/// to true, which create [ImageStreamCompleterHandle] to keep the completer
/// alive and is compatible with the [TickerMode] being off.
void _stopListeningToStream({bool keepStreamAlive = false}) {
if (!_isListeningToStream) {
return;
}
if (keepStreamAlive && _completerHandle == null && _imageStream?.completer != null) {
_completerHandle = _imageStream!.completer!.keepAlive();
}
_imageStream!.removeListener(_getListener());
_isListeningToStream = false;
}
该方法用于停止监听图片流(ImageStream),如果该状态对象附加了一个监听器。