I am displaying images from the internet in a vertical ListView
. I fetch the images using http.get
(not using cached network image cuz I do not want to cache the images). Then I insert the image Uint8List
into image.memory()
. What happens is that as the user scrolls the list and images are loading, the ram keeps increasing until the whole app crashes. Any ideas what to do ?
Yeah, this is the normal behavior. I don't know why exactly. My theory is that the images by default are disposed if the dart objects holding references to them are garbage collected rather then when the widgets are knocked off the widgets tree, but don't take my word for it- that's just my personal reasoning. It may be completely wrong.
To get around this, I use Extended Image pakcage It's constructors take a bool clearMemoryCacheWhenDispose
which disposes of images in RAM in scroll lists. You may do that or visit the package code to see how he's doing it, and replicate it.
Other advice I can give is to make sure your images are of the appropriate size. If you are loading your images locally check this part of the documentation to have different dimensions selected for different screen sizes https://flutter.dev/docs/development/ui/assets-and-images#loading-images
If you are fetching your images from network which is more likely, make sure their sizes are appropriate, and have different sizes served if you can. If you don't have control over that set cacheWidth
and cacheHeight
in Image.memory
these will reduce the cached image in memory (RAM). You see by default Flutter caches images in full resolution despite their apparent size in the device/app. For example if you want your image to display in 200x200 box then set cacheWidth
to 200 * window.devicePixelRatio.ceil()
you will find window
in dart:ui
, and if you only set cacheWidth
, the ratio of your images will remain true. Same true if you only set cacheHeight
. Also do use ListView.builder
as suggested.
I am disappointed at how little is said about this in Flutter docs. Most of the time people discover this problem when their apps start crashing. Do check your dev tools regularly for memory consumption. It's the best indicator out there.
Cheers and good luck
clearMemoryCacheWhenDispose
the image will not be cached in RAM so when you scroll away and return it'll be re-fetched. You can't have it both ways @mariaMad –
Bertabertasi cacheExtent
parameter in ListView.builder
it might help. With the above comment with clearMemoryCacheWhenDispose
it will be cached but disposed as the name suggests :) There is another parameter called enableMemoryCache
. Experiment with them and see what works best for you. –
Bertabertasi cache
that'll cache images for you in local storage if that's what you mean. I have clearMemoryCacheWhenDispose
set to true
, and cache
set to true
so my images are cached in local storage, and are only fetched from network when they are not avaible in local storage, and when the widgets are disposed they are evicted from RAM. Check your dev tools to have a better picture of what's going on there is even a tap to monitor network traffic. –
Bertabertasi I was having the same issue and found a fix thanks to @moneer!
Context:
Users in my app can create shared image galleries which can easily contain several hundred images. Those are all displayed in a SliverGrid widget. When users scrolled down the list, too many images were loaded into RAM and the app would crash.
Things I had already implemented:
- Image resizing on the server side and getting the appropriate sized images on the client based on the device pixel ratio and the tile size in the gallery
- I made sure that my image widgets were properly disposing when out of view, but the memory size kept building up as the user scrolled through all the images anyway
- Implement cacheHeight to limit the size of the cached image to the exact size of the displayed image
All these things helped but the app would eventually still crash every time the user scrolled down far enough.
The fix:
After some googling I stumbled upon this thread and tried the extended_image_package as @moneer suggested. Setting the clearMemoryCacheWhenDispose
flag to true fixed the issue of the app crashing as it was now properly clearing the images from memory when they were out of view. Hooray! However, in my app users can tap on an image and the app navigates to an image detail page with a nice Hero animation. When navigating back the image would rebuild and this would cause a rebuild 'flicker'. Not that nice to look at and kind of distracting.
I then noticed that there's also an enableMemoryCache
flag. I tried setting this to false and that seems to work nicely for me. The Network tab in Dart DevTools seems to show that all images are only fetched from the network once when scrolling up and down the gallery multiple times. The app does not crash anymore.
I'll have to more testing to see if this leads to any performance issues (if you can think of any please let me know).
The final code:
ExtendedImage.network(
_imageUrl,
cacheHeight: _tileDimension,
fit: BoxFit.cover,
cache: true, // store in cache
enableMemoryCache: false, // do not store in memory
enableLoadState: false, // hide spinner
)
I had a similar issue when I loaded images from files in a ListView.
[left-side: old code, right-side: new code]
The first huge improvement for me: not to load the whole list at once
ListView(children:[...])
--->ListView.builder(...)
.
The second improvement was that images are no longer loaded at full-size:
Image.file("/path")
--->Image.file("/path", cacheWidth: X, cacheHeight: Y)
These two things solved my memory problems completely
Ideally caching happens kind of by default after some conditions are fulfilled. So its upon your app to be responsible to handle and control how the caching will happen. Checkout this answer https://mcmap.net/q/940621/-how-android-http-cache-is-working
© 2022 - 2024 — McMap. All rights reserved.
ListView.builder()
? – Amberly