将FutureBuilder与NetworkImage结合使用的规范方法

时间:2018-07-26 22:01:06

标签: flutter

如果图像已经加载,我希望能够在单个微任务中获取网络图像。但是,对于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,以防止我们通过完成程序来传递它?

1 个答案:

答案 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(() { });
    }
  }
}