dazza5000
dazza5000

Reputation: 7628

How to fix Listview scrolling jank when loading images from network

I am loading images using Image.network for each item in a list using the following code:

Image getEventImageWidget(AustinFeedsMeEvent event) {
return event.photoUrl.isNotEmpty ?
          Image.network(
            event.photoUrl,
            width: 77.0,
            height: 77.0,
          ) : Image.asset(
            'assets/ic_logo.png',
            width: 77.0,
            height: 77.0,
          );
}

When I scroll up and down, the list sometimes hangs when loading the images. Is there a way I can load the images on a background thread? What can I do to help fix scrolling performance?

NOTE: When I looked back at this, I found that the images that I was using were really large.

Upvotes: 14

Views: 11270

Answers (5)

kenma
kenma

Reputation: 51

I see the same issue, when there are large images in the list.

To resolve the issue, I shrink the image size by ResizeImage.

// Specify the widht of the ListView. 
// If it is unknown, it is reasonable to use device width.
final width = MediaQuery.of(context).size.width;

Image(
  image: ResizeImage(NetworkImage('http://xxxxx/huge-image.jpg'),
   width: width),
);

Upvotes: 0

diegoveloper
diegoveloper

Reputation: 103541

Are you sure your images are not very heavy? Check the size of the images first. Also you can use the package named: cache_network_image

It's very simple :

new Image(image: new CachedNetworkImageProvider(url))

UPDATE (Package was updated)

CachedNetworkImage(
        imageUrl: "http://via.placeholder.com/350x150",
        placeholder: (context, url) => new CircularProgressIndicator(),
        errorWidget: (context, url, error) => new Icon(Icons.error),
     ),

Upvotes: 5

Jonah Williams
Jonah Williams

Reputation: 21441

There are two was to speed up the rendering of your ListView of images.

The first is to set the cacheExtent property to a larger value in your ListView constructor. This property controls how much offscreen widgets are rendered, and will help by causing the rendering to start a bit sooner.

The second is to pre-cache your images using precacheImage. Flutter has an in-memory cache, so it is generally to necessary to cache everything to disk to get good read performance. Instead, you can ask Flutter to download these images ahead of time so that they are ready when the widget is built. For example, if you have a list of urls of your image, then in an initState method you could ask Flutter to cache all of them.

final List<String> imageUrls = [ /* ... */ ];

@override
void initState() {
  for (String url in imageUrls) {
    precacheImage(new NetworkImage(url), context);
  }
  super.initState();
}

Upvotes: 8

Jacob Soffer
Jacob Soffer

Reputation: 304

You can create a Stateful Widget that creates the ListView with placeholder images, then have it have an async method you call after build() that loads the images from network (one by one) and then changes the state of the previously mentioned widget to replace the placeholder with the correct image. As a bonus, you can create a cache that stores the images so they don't have to be downloaded each time the ListView enters scope (here you would have the async method look in the cache for the image and if it doesn't find it there, download it).

As a side note, this would obviously require giving each of the images in the ListView an index.

Upvotes: 0

blaneyneil
blaneyneil

Reputation: 3232

you can also use:

FadeInImage.assetNetwork(
  placeholder: 'assets/ic_logo.png',
  image: event.photoUrl,
  height: 77.0,
  width: 77.0,
  fit: BoxFit.cover,
  fadeInDuration: new Duration(milliseconds: 100),
),

but yeah, per diegoveloper, you sure your images aren't huge? Listview has no problem rendering anything that's close to reasonable in size.

Upvotes: 1

Related Questions