我有 2 个 rest API,一个用于数据,另一个用于图像。所以我这样做:
- 获取数据。
- 遍历数据。
- 在每个数据循环中,然后获取图像。
但是发生的是所有加载的图像总是使用列表的最后一个图像。我是这样做的:
//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/