Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preload network images using PageView

Tags:

flutter

dart

I'm currently developing a reader and using PageView to slide the page of images. How do I make the next page preload so that the user can slide to next page without waiting for the page to load? I don't want to download all the pages first because it will load the server and freezes my app. I just want to download just next one or two pages when the user browsing on current page.

Here is the excerpt of my code.

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

Thank you!

like image 390
Rururezu Avatar asked Jun 03 '18 03:06

Rururezu


3 Answers

Simple! Just set allowImplicitScrolling: true, // in PageView.builder

like image 81
Abhijeet Rai Avatar answered Nov 01 '22 20:11

Abhijeet Rai


I ended up using FutureBuilder and CachedNetworkImageProvider from the package cached_network_image to prefetch all the images. Here is my solution:

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),
  );
}
like image 12
Rururezu Avatar answered Nov 01 '22 20:11

Rururezu


As people mentioned before the cached_network_image library is a solution, but not perfect for my situation. There are a full page PageView(fit width and height) in my project, when I try previous code my PageView will show a blank page first, then show the image.

I start read PageView source code, finally I find a way to fit my personal requirement. The basic idea is change PageView source code's cacheExtent

This is description about how cacheExtent works:

The viewport has an area before and after the visible area to cache items that are about to become visible when the user scrolls.

Items that fall in this cache area are laid out even though they are not (yet) visible on screen. The cacheExtent describes how many pixels the cache area extends before the leading edge and after the trailing edge of the viewport.

Change flutter's source code directly is a bad idea so I create a new PrelodPageView widget and use it at specific place when I need preload function.

Edit:

I add one more parameter preloadPagesCount for preload multiple pages automatically.

https://pub.dartlang.org/packages/preload_page_view

like image 3
Neil G Avatar answered Nov 01 '22 20:11

Neil G