【问题标题】:Canonical way to use FutureBuilder with NetworkImage将 FutureBuilder 与 NetworkImage 一起使用的规范方法
【发布时间】:2019-01-03 23:55:54
【问题描述】:

如果图像已经加载,我希望能够在单个微任务中访问网络图像。但是,使用 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 来阻止我们通过一个完成器来传递它?

【问题讨论】:

    标签: flutter


    【解决方案1】:

    我将利用传递给ImageStream 侦听器的syncCall 参数,而不是使用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(() { });
        }
      }
    }
    

    【讨论】:

    • 这是一个不错的解决方案,感谢 Jonah。如果不使用 FutureBuilder,有几种解决方案。您还可以将图像本身设置为状态的一部分,并始终从侦听器调用 setState() (我更喜欢您的解决方案)。我希望我们可以使用 FutureBuilder,因为它为异步加载资源提供了一个很好的接口。
    • 实际上,几乎没有理由使用 FutureBuilder。正确使用一个无论如何都需要一个 StatefulWidget,此时您可能会使用一个标志。
    猜你喜欢
    • 2019-01-31
    • 2021-02-04
    • 2021-10-06
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多