dart - 如何根据先前下载的信息获取图像列表?

标签 dart flutter bloc

我有 2 个 rest API,一个用于数据,另一个用于图像。所以我这样做:

  1. 获取数据。
  2. 遍历数据。
  3. 在每个数据循环中,然后获取图像。

但是发生的是所有加载的图像总是使用列表的最后一个图像。我是这样做的:

//this is the list fetch
return     
    StreamBuilder(
      stream: myBloc.myList,
      builder: (context, AsyncSnapshot<List<Result>> snapshot){
          if (snapshot.hasData) {
            //build the list
            return buildList(snapshot);
          } else if (snapshot.hasError) {
            return Text(snapshot.error.toString());
          }
          return Center(child: CircularProgressIndicator());        
      },
    );



  Widget buildList(AsyncSnapshot<List<Result>> snapshot) {
return GridView.builder(
    itemCount: snapshot.data.length,
    gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
    itemBuilder: (BuildContext context, int index) {

      //fetch the image
      myBloc.fetchImageById(snapshot.data[index].Id.toString());

      return
      StreamBuilder(
        stream: myBloc.imageById,
        builder: (context, AsyncSnapshot<dynamic> snapshotImg){
            if (snapshotImg.hasData) {

              return 
              MyPlaceholder(
                imageProvider: snapshotImg.data,
                title: snapshot.data[index].name,
              );

            } else if (snapshotImg.hasError) {

              return 
              MyPlaceholder(
                imageProvider: null,
                title: snapshot.data[index].name,
              );

            }
            return Center(child: CircularProgressIndicator());        
        },
      );

    });

  }

这是我的 BLoC 类:

class MyBloc {
  final _repository = MyRepository();

  final _fetcher = PublishSubject<List<Result>>();
  final _fetcherImage = PublishSubject<dynamic>();

  Observable<List<Result>> get myList => _fetcher.stream;
  Observable<dynamic> get myImageById => _fetcherImage.stream;

  fetchResultList() async {
    List<Result> result = await _repository.fetchMyList();
    _fetcher.sink.add(result);
  }

  fetchImageById(String _id) async {
    dynamic imgBinary = await _repository.fetchImageById(_id);
    _fetcherImage.sink.add(imgBinary);
  }

  dispose() {
    _fetcher.close();
    _fetcherImage.close();
  }

}

final categoryBloc = CategoryBloc();

我错过了吗?在另一个 bloc 中不可能有 bloc Observable?

最佳答案

But what happens is all loaded image always the last image of the list.

您的 BLoC 中的图像只有一个流,因此您的 GridView 行中所有相应的 StreamBuilder 将仅更新快照中的最后一个值(并且您最终得到最后一个图像)。

如果您确定只有几张图片要在您的 GridView 中显示,您可以使用 fetchImageById() 方法为其创建一个流特定图像,保留对它的引用,然后返回它。然后您可以将返回的流传递给行 StreamBuilder 而不是 myBloc.imageById,这样您的行 StreamBuilders 将具有不同的数据源。加载图像后,您可以将其添加到该特定流(基于 id),并且您的行将仅使用该特定数据进行更新。一些代码:

//fetch the image
Observable<dynamic> imageStream =      myBloc.fetchImageById(snapshot.data[index].Id.toString());
      return StreamBuilder(
        stream: imageStream,
        builder: (context, AsyncSnapshot<dynamic> snapshotImg){
// rest of code

在您的 BLoC 中,您将拥有:

Map<String, PublishSubject<dynamic>> _backingImageStreams = HashMap()

Observable<dynamic> fetchImageById(String _id) {
    PublishSubject<dynamic> backingImgStream = _backingImageStreams[id];
    if (backingImgStream == null) {
        backingImgStream = PublishSubject<dynamic>();
        _backingImageStreams[id] = backingImgStream;
    }
    // i'm assuming that repository.fetchImageById() returns a Future ?!
    _repository.fetchImageById(_id).then((){
        _fetcherImage.sink.add(imgBinary);
    });
    return _fetcherImage.stream;
}

在更一般的情况下,我认为您需要将代码从 StreamBuilder 更改为 FutureBuilder。在您的小部件中,您将拥有:

Widget buildList(AsyncSnapshot<List<Result>> snapshot) {
   // ...
   itemBuilder: (BuildContext context, int index) {
      //fetch the image
      Future<dynamic> imageFuture = myBloc.fetchImageById(snapshot.data[index].Id.toString());
      return FutureBuilder(
            future: imageFuture,
            builder: (context, AsyncSnapshot<dynamic> snapshotImg){
   // rest of your current code

然后您需要更改 BLoC 方法 fetchImageById()。当你处理图像时,你会想要实现某种缓存,这样你会更有效率:

  • 如果您已经拥有相同的图像,则不要再次下载它(并快速向用户展示)
  • 不要一次加载所有图像并弄乱内存(或完全失败)

BLoC 代码:

class MyBloc {
    // remove the imageId observable

    // A primitive and silly cache. This will only make sure we don't make extra 
    // requests for images if we already have the image data, BUT if the user 
    // scrolls the entire GridView we will also have in memory all the image data. 
    // This should be replaced with some sort of disk based cache or something 
    // that limits the amount of memory the cache uses.
    final Map<String, dynamic> cache = HashMap();

    FutureOr<dynamic> fetchImageById(String _id) async {
        // atempt to find the image in the cache, maybe we already downloaded it
        dynamic image = cache[id];
        // if we found an image for this id then we can simply return it
        if (image != null) {
            return image;
        } else {
            // this is the first time we fetch the image, or the image was previously disposed from the cache and we need to get it
           dynamic image = // your code to fetch the image 
           // put the image in the cache so we have it for future requests
           cache[id] = image;
           // return the downloaded image, you could also return the Future of the fetch request but you need to add it to the cache
           return image;
        }
     }

如果您只想向用户显示图像,只需让您的 fetchImageById() 返回图像获取请求的 future (但每次构建小部件时您都会发出一个获取请求)。

关于dart - 如何根据先前下载的信息获取图像列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54140182/

相关文章:

dart - Flutter 从图库中选择多张图片

flutter - Dart 依赖注入(inject) - 为什么我不能将实例引用传递给类成员?

android - 什么时候应该在 Flutter 中使用 BLoC 模式?

flutter : How to center an icon next to hint text inside an input decoration?

firebase - NoSuchMethodError:类 'QuerySnapshot'没有实例方法 '[]'

flutter - 如何在 flutter 中使用 Paint() 在自定义 Paint 中编写文本

flutter - 缺口 BottomAppBar flutter

flutter - 业务逻辑和 UI 逻辑有什么区别?

Flutter:未处理的异常:错误状态:调用关闭后无法添加新事件(不同情况)

firebase - 如何查看收藏夹中的内容?