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!
Simple! Just set allowImplicitScrolling: true, // in PageView.builder
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),
);
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With