Hi,
Allez gezellig! Thanks for creating this plugin. I was really concerned about my app crashes and then I did memory profiling on the app. And I am attaching the screenshots of heap dumps here. I have used Cached Network Image in my entire project and have not used Network. Image or any other plugin. As you can see that the images/ Cached Network Image is responsible for app crashes and out of memory errors. I have also read the similar issues here in issues section for the plugin. And now I am reading even Network.Image or ImagePicker causes the same kind of behavior if used without cacheWidth/width and cacheHeight. I think flutter team should update their official docs for these plugins/libraries so that unexpected behavior is not triggered causing the production app to crash if developers used these plugins without any recommendations to reduce memory consumption in the heap.
It's also important to mention in Readme for this plugin to use all recommended measures such as memWidth, memHeight or use the 2.3.0 beta version so that future developers can use this plugin effectively to add multiple images in the app. It would be also helpful if you can elaborate on all these recommended measures here or update the Readme so that it can help the future developers.
I also tried using 2.3.0-beta.1 in android app because that't the latest version on stable flutter channel. I also tried adding memWidth and memHeight parameters to CachedNetworkImage. But to no avail. That's why need some recommendations from the contributors. I saw the code for CachedNetwork image too and it's using default cache manager from flutter cache manager package if not provided with cache manager as parameter. The only thing that now I could try is to limit the maximum number of cache objects for Cache Manager which is passed as parameter to the Cached Network Image.
So my question to the team are as follows:
Important update: I also tried using global cache manager with maxNumberOfObjects limited to some number which could be stored in cache. Then I shared this BaseCacheManager instance as parameter to all instances of CachedNetworkImage in my code as shown as follows:
class ImageCacheManager extends BaseCacheManager {
static const key = "MyApp_Cache"; //just use some unique name instead of MyApp
static ImageCacheManager _instance;
factory ImageCacheManager() {
if (_instance == null) {
_instance = new ImageCacheManager._();
}
return _instance;
}
ImageCacheManager._() : super(key,
maxAgeCacheObject: Duration(days: 1),
maxNrOfCacheObjects:20
);
Future
var directory = await getTemporaryDirectory();
return path.join(directory.path, key);
}
}
ImageCacheManager ImageCacheStorage=new ImageCacheManager();
CachedNetworkImage(
imageUrl: "",
placeholder: (context, url) =>
Image.memory(kTransparentImage),
errorWidget: (context, url, error) =>
Image.memory(kTransparentImage),
fadeInCurve: Curves.easeIn,
memCacheWidth: 500,
memCacheHeight:500,
cacheManager: ImageCacheStorage,
)
But still the memory used in the heap is increasing to 500MB-600MB which doesn't make any sense to me if I am limiting the maxNumber of objects stored in cache to 20.
Then I tried using Image.network with cacheWidth and cacheHeight parameters and setting imageCache size to 50MB as follows:
imageCache.maximumSizeBytes=50000000;
Network Image is behaving in correct way and heap size doesn't increase beyond 70-80 MB.
For now, I would prefer to use NetworkImage to avoid app crashes even if CachedNetworkImage has positive points such as errorWidget and placeholderWidget.
I hope that the contributors could comment on the issue for how to use the CachedNetworkImage effectively in case of large number of images in the app without increasing the heap size to a large amount so that it can help me and other future developers to take the right decision :)
@pranavkpr1 _imageCache.clearLiveImages(); Can reduce a lot of memory

@pranavkpr1 _imageCache.clearLiveImages(); Can reduce a lot of memory
Hi @pushuhengyang , Thanks for the solution, but can you please help us where to use this code. We are also using cachednetwork image and facing the OOM in iOS (Low-end devices) mostly.
I am also experiencing extreme memory issues.
I prefer this library because it has fade in animations and provides good functionality,
but it can easily bring my app to use over 1GB of RAM and beyond.
I have experienced the app using so much RAM that the OS starts killing off all background apps to eventually kill off my app in a last attempt to not die, because images are not freed. External memory just goes on to consume all available RAM.
My app allows accessing large images up to 8k etc and if the user loads a significant amount of them,
this will lead to the app getting killed by the OS or like others mentioned, just crashing.
I understand that keeping images in RAM is important so they dont have to be loaded again and again and this is also very useful in the case of my app, but that there is literally no limit to RAM consumption is rather detrimental.
This is a rather big issue for me right now, and I would love it if there was a fix.
@pranavkpr1 _imageCache.clearLiveImages(); Can reduce a lot of memory
Hi @pushuhengyang , Thanks for the solution, but can you please help us where to use this code. We are also using cachednetwork image and facing the OOM in iOS (Low-end devices) mostly.
Judge every time you load the image

@pushuhengyang and where would you call _checkMemory()?
Can you provide use with a minimal working example?
@pushuhengyang and where would you call _checkMemory()?
Can you provide use with a minimal working example?

It's best to encapsulate a widget
Hi all.
In version 2.3.1 there have been some improvements in memory usage. Next to that you can set the height and/or width of the image in memory using memCacheWidth/memCacheHeight. Setting these to a reasonable size should help a large part of the memory issues.
https://pub.dev/documentation/cached_network_image/latest/cached_network_image/CachedNetworkImage-class.html
Hi Rene,
As I mentioned in this post, I have already tried using dev version of CachedNetworkImage which is the new version you are asking to try. I have already tried adding memCacheWidth/ memCacheHeight as mentioned in the original post but still I was facing the same memory issue when I was performing memory profiling as shown in the attached screenshots in the original post. According to my experience, we can't use this package in e-commerce or social media kind of app where there is use case of showing large number of images as it can trigger crashes due to OOM issue. Hope to hear from you soon.
Sorry didn't read it good enough. I have to take some time to dig into this.
I am also facing the same issue. On the simulator, it just works and on the real iPhone, it crashes.
For the time being, I switched to https://github.com/humblerookie/optimized_cached_image which is very similar to flutter_cached_network_image and in fact, internally uses octo_image.
One more thing I noticed is when providing memCacheWidth/memCacheHeight in flutter_cached_network_image it stops crashing but flutter's ResizeImage cacheWidth/cacheHeight don't care about the image fit we mention for the CachedNetworkImage
Source
https://github.com/flutter/flutter/issues/52802
https://github.com/flutter/flutter/pull/64352
@pushuhengyang and where would you call _checkMemory()?
Can you provide use with a minimal working example?
It's best to encapsulate a widget
This does not work for me. I execute this code on every build of a CachedNetworkImage
print(_imageCache.liveImageCount);
print(_imageCache.currentSize);
print(_imageCache.currentSizeBytes);
This results in
flutter: 1
flutter: 1
flutter: 3616960
But my app still crashes. I wil now start with profiling to check why this is happening.
When profiling I see a very big spike in memory usage. I have the same problem with Image.network:
https://github.com/flutter/flutter/issues/47378#issuecomment-692060368
This is our workaround for the moment. We wrote our own widget that downscales and caches the downscaled image to the cache
for this we will need the devicePixelRatio but that is coming from MediaQuery.of so you will need to fetch that value before you are using this widget. (We save this value in our config when MaterialApp is build. so we can always access this because the devicePixelRatio is will not change. )
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:pedantic/pedantic.dart';
import 'package:flutter/material.dart';
class MyProjectBetterImageWidget extends StatefulWidget {
final String imageUrl;
final BoxFit fit;
final double width;
final double height;
final Widget placeholder;
const MyProjectBetterImageWidget({
@required this.imageUrl,
@required this.fit,
@required this.width,
@required this.height,
@required this.placeholder,
Key key,
}) : super(key: key);
@override
_MyProjectBetterImageWidgetState createState() => _MyProjectBetterImageWidgetState();
}
class _MyProjectBetterImageWidgetState extends State
var _isLoading = false;
var _hasError = false;
Uint8List _image;
Uint8List get image => _image;
bool get showPlaceholder => _hasError || _isLoading || _image == null;
@override
void initState() {
super.initState();
_getImage();
}
@override
void didUpdateWidget(MyProjectBetterImageWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.imageUrl != widget.imageUrl) {
_getImage();
}
}
Future
final originalUrl = widget.imageUrl;
final widgetWidth = widget.width;
final widgetHeight = widget.height;
if (originalUrl == null || originalUrl.endsWith('unknown.jpg')) {
return;
}
if (widgetWidth == double.infinity || widgetHeight == double.infinity || widgetWidth == null || widgetHeight == null) {
MyProjectLogger.logDebug('IMAGE-ERROR: $originalUrl ($widgetWidth/$widgetHeight');
setState(() => _hasError = true);
return;
}
try {
setState(() {
_isLoading = true;
_hasError = false;
});
final width = (widgetWidth * FlavorConfig.instance.devicePixelRatio).toInt();
final height = (widgetHeight * FlavorConfig.instance.devicePixelRatio).toInt();
final url = '$originalUrl?w=$width&h=$height';
var fileInfo = await DefaultCacheManager().getFileFromCache(url);
var fromCache = true;
if (!mounted) return;
if (fileInfo == null) {
fromCache = false;
fileInfo = await DefaultCacheManager().downloadFile(url);
}
// ignore: invariant_booleans
if (!mounted) return;
if (fromCache) {
_image = await fileInfo.file.readAsBytes();
setState(() {
_isLoading = false;
_hasError = false;
});
return;
}
final bytes = await fileInfo.file.readAsBytes();
final codec = await instantiateImageCodec(
bytes,
targetWidth: width >= height ? width : null,
targetHeight: height > width ? height : null,
);
final frame = await codec.getNextFrame();
final data = await frame.image.toByteData(format: ImageByteFormat.png);
_image = data.buffer.asUint8List();
if (!fromCache) {
unawaited(_cacheImage(url));
}
} catch (e) {
MyProjectLogger.logError(message: 'Failed to parse image: $originalUrl', error: e);
_hasError = true;
} finally {
_isLoading = false;
}
// ignore: invariant_booleans
if (mounted) {
setState(() {});
}
}
Future
try {
await DefaultCacheManager().putFile(url, _image, fileExtension: 'png');
} catch (e) {
MyProjectLogger.logError(message: 'Failed to cache image: $url', error: e);
}
}
@override
Widget build(BuildContext context) {
if (showPlaceholder) return widget.placeholder;
return Image.memory(
image,
width: widget.width,
height: widget.height,
fit: widget.fit,
);
}
}
Thanks for the workaround, @vanlooverenkoen! Are you using that in production now? No memory errors? I was trying to adapt it to my project and I have a couple of questions.
Questions:
errorWidget. I can add it, maybe you evolved that already.Also, do you think this widget is relevant enough to move this to its own repo, so we don't divert the conversation from the main issue about flutter_cached_network_image?
I've got the same issue, the app loading about 100s images then exit suddenly (randomly, sometimes it crashes on about load 20-30 images,sometimes 200+ images), I read the log, the message about not ensure folder exists /mnt/shell/emulated/0/Android/data/..../caches and /mnt/shell/emulated/0/Android/data/..../files
I've added read, write externalcard permission, and the app not crash and exit suddenly.
If I didnt add the permission, why the app still run and crash randomly, why the lib not require permission at the first run.
updated: my app still randomly crash
@renefloor adding the memCacheWidth/memCacheHeight do reduce the memory hike as I check in Memory dart tools. But setting this value causing the image to be blur. Or we can say it pixelate. Is there any solution for it?
@RaashVision at what value did you set it? If you set it really small I can imaging that it becomes pixelated.
It is better to take other measures, for example that you don't show more than 10 images at the same time.
One way is for example by using ListView.builder: https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html
I get the same problem too. I have a listview builder and hundreds of png images on it. But when app try to load listview with images and scroll on listview, memory goes up to 2GB. App starts 300MB of memory usage but when app try to load images, memory starts to increase, and on iphone 11 pro kills the app around 2gb of RAM usage, iphone 6 kills the app around 650 Mb of RAM usage. I tried various methods but somehow couldn't solve the memory problem.
CachedNetworkImage(
imageUrl: image_url,
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Image.asset(noImage),
),
@halkportal970 Did you find any solution my case is the same as yours
@AdnanKazi Yes I found a solution.
I added memCacheWidth to all CachedNetworkImage widgets.
This realy reduced memory usage. Because my images width and height properties are very big.
But this is not a very good solution.
In web service side we must decrease images width and height according to mobile.
CachedNetworkImage(
imageUrl: image_url,
memCacheWidth: (Get.width * 0.6).toInt(),
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Image.asset(noImage),
),
@halkportal970
Thank you so much for your advice in the meantime I have also found the solution which doesn't crash the app
Image.network('url',
cacheHeight: widget.cacheHeight,
cacheWidth: widget.cacheWidth,
fit: BoxFit.cover,
errorBuilder: (context, url, error) => Icon(Icons.error),
)
I will also try your solution as well as your solution looks better one
Having the same issue using this plugin. It raises a EXC_RESOURCE RESOURCE_TYPE_MEMORY on the thread that has name = io.flutter.1.io.
@pranavkpr1 _imageCache.clearLiveImages(); Can reduce a lot of memory
It works!
I just tried an example with setting memCacheHeight and for me it largely reduced the memory footprint of the app.
@pranavkpr1 Hi... Can you pls share an example how u solved this issue........
I am also facing the same issue
@gathodeharrkirat did you try setting memCacheHeight?
For example:
CachedNetworkImage(
height: 200,
width: 200,
memCacheHeight: 200,
imageUrl: url,
),
I am currently debugging an app ready for production but getting reports of crashes on older iphones (1gb RAM).
They are mostly caused by images not being correctly optimized by our client (like 4k pics for previews...).
Limiting them with memCacheHeight and memCacheWidth should do the trick for most, but it's kind of a sledgehammer method. Source Images should be optimized, not cut like this, as this also stretches non fitting resolutions.
A good idea is also using your own global cache manager instance, listening to low memory warning notifications from the system and dumping the cache to prevent crashes.
This is overall a very classic "limitation" of GPU frameworks and game engines. Pictures and Text (which is rendered and uploaded as bitmaps) have to be highly optimized and manually controlled.
A 1kb picture can easily take 20mb+ RAM. Look up the tech and math behind it if you professionally build apps with GPU frameworks!
Is it possible to add same memCacheHeight option to CachedNetworkImageProvider?
My usecase is that I want to preload some images before showing them and to do so I need ImageProvider in precacheImage() method.
Or maybe an option to get ImageProvider from CachedNetworkImage object as it's done in Image class (image property).
Most helpful comment
I am also experiencing extreme memory issues.
I prefer this library because it has fade in animations and provides good functionality,
but it can easily bring my app to use over 1GB of RAM and beyond.
I have experienced the app using so much RAM that the OS starts killing off all background apps to eventually kill off my app in a last attempt to not die, because images are not freed. External memory just goes on to consume all available RAM.
My app allows accessing large images up to 8k etc and if the user loads a significant amount of them,
this will lead to the app getting killed by the OS or like others mentioned, just crashing.
I understand that keeping images in RAM is important so they dont have to be loaded again and again and this is also very useful in the case of my app, but that there is literally no limit to RAM consumption is rather detrimental.
This is a rather big issue for me right now, and I would love it if there was a fix.