代码之家  ›  专栏  ›  技术社区  ›  stuckedunderflow

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

  •  1
  • stuckedunderflow  · 技术社区  · 6 年前

    我有两个RESTAPI,一个用于数据,另一个用于图像。所以我这样做:

    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());        
            },
          );
    
        });
    
      }
    

    这是我的布洛克班:

    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();
    

    我错过了吗?不可能在另一个集团内观察到集团?

    1 回复  |  直到 6 年前
        1
  •  1
  •   user Roman    6 年前

    但所发生的是所有加载的图像始终是 名单。

    对于块中的图像,您有一个单独的流,因此GridView行中所有相应的StreamBuilder都将只使用快照中的最后一个值进行更新(最后一个图像即结束)。

    如果你确定你只有一些图片 如果希望在GridView中显示,可以使用fetchImageByID()方法为特定图像创建流,保留对该图像的引用,然后返回该图像。然后,您可以将返回的流传递给row streambuilder,而不是mybloc.imagebyid,这样,您的rows streambuilder将具有不同的数据源。加载图像后,您可以将其添加到特定流(基于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
    

    在您的集团中,您将拥有:

    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;
    }
    

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

    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()。当您处理图像时,您将希望实现某种缓存,这样您将更高效:

    • 如果你已经有了相同的图片,不要再下载它(并且要快速地将它显示给用户)
    • 不能一次加载所有图像并使内存混乱(或完全失败)

    集团代码:

    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()从图像获取请求返回未来(但每次构建小部件时都会发出获取请求)。

    推荐文章