使用PageView预加载网络图片

20

我目前正在开发一个阅读器,并使用PageView来滑动图像页面。如何使下一页预加载,以便用户可以在不等待页面加载的情况下滑动到下一页?我不希望先下载所有页面,因为这样会加载服务器并使我的应用程序崩溃。我只想在用户浏览当前页面时下载下一页或下两页。

以下是我的代码摘录。

PageController _controller;
ZoomableImage nextPage;

Widget _loadImage(int index) {
  ImageProvider image = new CachedNetworkImageProvider("https://example.com/${bookId}/${index+1}.jpg}");
  ZoomableImage zoomed = new ZoomableImage(
              image, 
              placeholder: new Center(
                child: CupertinoActivityIndicator(),
              ),
            );
    return zoomed;
  }

@override
Widget build(BuildContext context) {
  return new Scaffold(
    body: new Container(
      child: PageView.builder(
        physics: new AlwaysScrollableScrollPhysics(),
        controller: _controller,
        itemCount: book.numPages,
        itemBuilder: (BuildContext context, int index) {
          return index == 0 || index == 1 ? _loadImage(index) : nextPage;
        },
        onPageChanged: (int index) {
          nextPage = _loadImage(index+1);
        },
      ),
    ),
  );
}

谢谢!

3个回答

39

简单!只需设置allowImplicitScrolling: true, // in PageView.builder


我简直无法相信这个回答是如此简短、简单和直接,它完美地解决了我类似的问题,谢谢。 - Rageh Azzazy
不,答案不准确。allowImplicitScrolling只保留当前页面的前一页和后一页。一旦你滚动到第四页,第一页将被丢弃。此外,它不支持预加载。目前,除非使用像preload_pageview这样的第三方包,否则无法实现预加载。 - undefined

12

我最终使用了来自cached_network_image包的FutureBuilderCachedNetworkImageProvider来预取所有图像。 这是我的解决方案:

PageController _controller;
ZoomableImage currPage, nextPage;

Future<List<CachedNetworkImageProvider>> _loadAllImages(Book book) async {
  List<CachedNetworkImageProvider> cachedImages = [];
  for(int i=0;i<book.numPages;i++) {
    var configuration = createLocalImageConfiguration(context);
    cachedImages.add(new CachedNetworkImageProvider("https://example.com/${bookId}/${index+1}.jpg}")..resolve(configuration));
  }
  return cachedImages;
}

FutureBuilder<List<CachedNetworkImageProvider>> _futurePages(Book book) {
  return new FutureBuilder(
    future: _loadAllImages(book),
    builder: (BuildContext context, AsyncSnapshot snapshot){
      if(snapshot.hasData) {
        return new Container(
          child: PageView.builder(
            physics: new AlwaysScrollableScrollPhysics(),
            controller: _controller,
            itemCount: snapshot.data.length,
            itemBuilder: (BuildContext context, int index) {
              ImageProvider image = snapshot.data[index];
              return new ZoomableImage(
                image, 
                placeholder: new Center(
                  child: CupertinoActivityIndicator(),
                ),
              );
            },
            onPageChanged: (int index) {},
          ),
        );
      } else if(!snapshot.hasData) return new Center(child: CupertinoActivityIndicator());
    },
  );
}

@override
Widget build(BuildContext context) {
  return new Scaffold(
    body: _futurePages(widget.book),
  );
}

有没有人能告诉我 CachedNetworkImageProviderallowImplicitScrolling: true, // in PageView.builder 之间的区别? - Yeasin Sheikh
1
不考虑细节,这个解决方案已经有2.5年的历史了,所以也许allowImplicitScrolling是后来添加的。 - RobDil

3
人们之前提到过 cached_network_image 库是一个解决方案,但对于我的情况并不完美。在我的项目中有一个全屏的 PageView(适应宽度和高度),当我尝试之前的代码时,我的 PageView 将首先显示一个空白页面,然后再显示图片。
我开始阅读 PageView 源代码,最终我找到了一种适合我的要求的方法。基本思路是更改 PageView 源代码的 cacheExtent
这是关于 cacheExtent 如何工作的描述:

视口在可见区域之前和之后有一个区域来缓存即将在用户滚动时变得可见的项。

落入此缓存区域的项被布局,即使它们当前不可见。cacheExtent 描述了缓存区域在视口的引导边缘和尾随边缘之前和之后延伸多少像素。

直接更改 flutter 的源代码是一个坏主意,所以我创建了一个新的 PrelodPageView widget 并在需要预加载功能的特定位置使用它。 编辑: 我添加了一个名为 preloadPagesCount 的参数,以自动预加载多个页面。 https://pub.dartlang.org/packages/preload_page_view

这个之前是可以运行的,但我换了电脑后现在出错了。 - xbadal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接