如果图像已经加载,我希望能够在单个微任务中获取网络图像。但是,对于NetworkImage和FutureBuilder中可用的当前API,这似乎是不可能的。
这是我们通常将两者连接的方式:
NetworkImage imageProvider = getSomeNetworkImage(id);
Completer<ui.Image> completer = Completer<ui.Image>();
imageProvider.resolve(ImageConfiguration()).addListener(
(ImageInfo info, _) => completer.complete(info.image));
return FutureBuilder<ui.Image>(
future: completer.future,
builder: (BuildContext futureBuilderContext, AsyncSnapshot<ui.Image> snapshot) {
if (!snapshot.hasData) {
return _buildPlaceholder();
} else {
return _buildActual(context, snapshot.data, imageProvider);
}
},
);
如果图像已经存在,addListener()立即调用completer.complete()。但是,FutureBuilder基于completer.future,后者直到下一个微任务才完成。因此,即使图像可用,占位符也会暂时显示。
避免这种情况的最佳方法是什么?也许,imageProvider应该公开一个Future,以防止我们通过完成程序来传递它?
答案 0 :(得分:2)
我将使用传递到syncCall
的侦听器的ImageStream
参数,而不是使用FutureBuilder。这将告诉您图像是否立即解析,这意味着它已被缓存。否则,您可以调用setState并在完成时触发重建。
class Example extends StatefulWidget {
const Example({Key key, this.image, this.child}): super(key: key);
final ImageProvider image;
final Widget child;
@override
State createState() => new ExampleState();
}
class ExampleState extends State<Example> {
bool _isImageLoaded = false;
@override
void initState() {
widget.image
.resolve(const ImageConfiguration)
.addListener(_handleResolve);
super.initState();
}
@override
Widget build(BuildContext context) {
// if syncCall = true, then _handleResolve will have already been called.
if (_isImageLoaded)
return new Image(widget.image);
return widget.child;
}
void _handleResolve(ImageInfo info, bool syncCall) {
_isImageLoaded = true;
if (!syncCall) {
// we didn't finished loading immediately, call setState to trigger frame
setState(() { });
}
}
}