<h1 id="how-to-cache-network-images-in-flutter">How to Cache Network Images in Flutter</h1> <p>Loading and displaying images from the network is a common requirement in most apps. However, repeatedly downloading the same images is inefficient and can lead to a poor user experience. In this guide, we'll explore how to effectively cache network images in Flutter using the popular <code>cached_network_image</code> package.</p> <h2 id="why-cache-images">Why Cache Images?</h2> <p>Caching network images provides several benefits:</p> <ol> <li><strong>Reduced Data Usage</strong>: Images are downloaded only once and stored locally</li> <li><strong>Faster Load Times</strong>: Cached images load instantly from local storage</li> <li><strong>Offline Support</strong>: Previously loaded images can be displayed without internet</li> <li><strong>Improved Performance</strong>: Reduces network requests and memory usage</li> </ol> <p><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8IS0tIEJhY2tncm91bmQgLS0+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjUwMCIgaGVpZ2h0PSIzMDAiIGZpbGw9IiNmYWZhZmEiLz4KICAKICA8IS0tIFBob25lIE91dGxpbmUgMSAtLT4KICA8cmVjdCB4PSI1MCIgeT0iNTAiIHdpZHRoPSIxMjAiIGhlaWdodD0iMjAwIiByeD0iMTAiIHJ5PSIxMCIgZmlsbD0iI2YwZjBmMCIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8cmVjdCB4PSI2MCIgeT0iNzAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSIjZGRkIiBzdHJva2U9IiNiYmIiIHN0cm9rZS13aWR0aD0iMSIvPgogIDx0ZXh0IHg9IjExMCIgeT0iMTIwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMTQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM4ODgiPkxvYWRpbmcuLi48L3RleHQ+CiAgPHRleHQgeD0iMTEwIiB5PSI2MCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNTU1Ij5GaXJzdCBMb2FkPC90ZXh0PgogIDxwYXRoIGQ9Ik0gODAgMTgwIEwgMTQwIDE4MCIgc3Ryb2tlPSIjMjE5NkYzIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIxMTAiIHk9IjE5MCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMjE5NkYzIj5Eb3dubG9hZGluZyBmcm9tIFVSTDwvdGV4dD4KICAKICA8IS0tIEFycm93IC0tPgogIDxsaW5lIHgxPSIxODAiIHkxPSIxNTAiIHgyPSIyMDAiIHkyPSIxNTAiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHBvbHlnb24gcG9pbnRzPSIxOTUsMTQ1IDIwNSwxNTAgMTk1LDE1NSIgZmlsbD0iIzMzMyIvPgogIAogIDwhLS0gU3RvcmFnZSAtLT4KICA8cmVjdCB4PSIyMDAiIHk9IjEyMCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjYwIiByeD0iNSIgcnk9IjUiIGZpbGw9IiNENEVFRkYiIHN0cm9rZT0iIzc2QzhGRiIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjQwIiB5PSIxNTAiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzIxOTZGMyI+Q2FjaGUgU3RvcmFnZTwvdGV4dD4KICA8Y2lyY2xlIGN4PSIyMzAiIGN5PSIxNDAiIHI9IjEwIiBmaWxsPSIjZmZmIiBzdHJva2U9IiM3NkM4RkYiIHN0cm9rZS13aWR0aD0iMSIvPgogIDxwYXRoIGQ9Ik0gMjI1IDE0MCBMIDI0MCAxNDAiIHN0cm9rZT0iIzc2QzhGRiIgc3Ryb2tlLXdpZHRoPSIxIi8+CiAgPHBhdGggZD0iTSAyMzAgMTM1IEwgMjMwIDE0NSIgc3Ryb2tlPSIjNzZDOEZGIiBzdHJva2Utd2lkdGg9IjEiLz4KICAKICA8IS0tIFBob25lIE91dGxpbmUgMiAtLT4KICA8cmVjdCB4PSIzMjAiIHk9IjUwIiB3aWR0aD0iMTIwIiBoZWlnaHQ9IjIwMCIgcng9IjEwIiByeT0iMTAiIGZpbGw9IiNmMGYwZjAiIHN0cm9rZT0iI2FhYSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgCiAgPCEtLSBJbWFnZSBWaXN1YWxpemF0aW9uIC0tPgogIDxkZWZzPgogICAgPHBhdHRlcm4gaWQ9ImltYWdlUGF0dGVybiIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiPgogICAgICA8aW1hZ2UgaHJlZj0iZGF0YTppbWFnZS9zdmcreG1sLCUzQ3N2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMDAnIGhlaWdodD0nMTAwJyB2aWV3Qm94PScwIDAgMTAwIDEwMCclM0UlM0NjaXJjbGUgY3g9JzUwJyBjeT0nNTAnIHI9JzMwJyBmaWxsPSclMjMyMTk2RjMnLyUzRSUzQ3BhdGggZD0nTSAyMCA1MCBRIDUwIDEwIDgwIDUwIFQgODAgODAnIHN0cm9rZT0nd2hpdGUnIHN0cm9rZS13aWR0aD0nMycgZmlsbD0nbm9uZScvJTNFJTNDL3N2ZyUzRSIgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIC8+CiAgICA8L3BhdHRlcm4+CiAgPC9kZWZzPgogIDxyZWN0IHg9IjMzMCIgeT0iNzAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJ1cmwoI2ltYWdlUGF0dGVybikiIHN0cm9rZT0iI2JiYiIgc3Ryb2tlLXdpZHRoPSIxIi8+CiAgPHRleHQgeD0iMzgwIiB5PSI2MCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNTU1Ij5TdWJzZXF1ZW50IExvYWQ8L3RleHQ+CiAgPHBhdGggZD0iTSAzNTAgMTgwIEwgNDEwIDE4MCIgc3Ryb2tlPSIjNENBRjUwIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIzODAiIHk9IjE5MCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNENBRjUwIj5Mb2FkaW5nIGZyb20gQ2FjaGU8L3RleHQ+CiAgCiAgPCEtLSBBcnJvdyBmcm9tIHN0b3JhZ2UgdG8gcGhvbmUgLS0+CiAgPGxpbmUgeDE9IjI5MCIgeTE9IjE1MCIgeDI9IjMxMCIgeTI9IjEyMCIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1kYXNoYXJyYXk9IjUsNSIvPgogIDxwb2x5Z29uIHBvaW50cz0iMzA1LDEyNSAzMTUsMTE1IDMxMCwxMzAiIGZpbGw9IiMzMzMiLz4KICAKICA8IS0tIEJlbmVmaXRzIC0tPgogIDxyZWN0IHg9IjgwIiB5PSIyNjAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMjAiIHJ4PSI1IiByeT0iNSIgZmlsbD0iI0ZGRThFMSIgc3Ryb2tlPSIjRkY3MDQzIiBzdHJva2Utd2lkdGg9IjEiLz4KICA8dGV4dCB4PSIxMzAiIHk9IjI3NCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjRkY1NzIyIj5SZWR1Y2VkIERhdGEgVXNhZ2U8L3RleHQ+CiAgCiAgPHJlY3QgeD0iMTkwIiB5PSIyNjAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMjAiIHJ4PSI1IiByeT0iNSIgZmlsbD0iI0UxRjVGRSIgc3Ryb2tlPSIjMDNBOUY0IiBzdHJva2Utd2lkdGg9IjEiLz4KICA8dGV4dCB4PSIyNDAiIHk9IjI3NCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMDI4OEQxIj5GYXN0ZXIgTG9hZCBUaW1lczwvdGV4dD4KICAKICA8cmVjdCB4PSIzMDAiIHk9IjI2MCIgd2lkdGg9IjEwMCIgaGVpZ2h0PSIyMCIgcng9IjUiIHJ5PSI1IiBmaWxsPSIjRjFFOEZGIiBzdHJva2U9IiM5QzI3QjAiIHN0cm9rZS13aWR0aD0iMSIvPgogIDx0ZXh0IHg9IjM1MCIgeT0iMjc0IiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMTAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM3QjFGQTIiPk9mZmxpbmUgU3VwcG9ydDwvdGV4dD4KICAKICA8IS0tIENvZGUgSW5kaWNhdGlvbiAtLT4KICA8dGV4dCB4PSIyNTAiIHk9IjMwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMzMzIj5JbWFnZSBDYWNoaW5nIFdvcmtmbG93PC90ZXh0PgogIAogIDx0ZXh0IHg9IjI0MCIgeT0iMjMwIiBmb250LWZhbWlseT0ibW9ub3NwYWNlIiBmb250LXNpemU9IjgiIGZpbGw9IiM2NjYiIHRleHQtYW5jaG9yPSJtaWRkbGUiPkNhY2hlZE5ldHdvcmtJbWFnZShpbWFnZVVybDogdXJsKTwvdGV4dD4KICA8dGV4dCB4PSIyNDAiIHk9IjI0MyIgZm9udC1mYW1pbHk9Im1vbm9zcGFjZSIgZm9udC1zaXplPSI4IiBmaWxsPSIjNjY2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5wbGFjZWhvbGRlcjogKGNvbnRleHQsIHVybCkgPT4gQ2lyY3VsYXJQcm9ncmVzc0luZGljYXRvcigpPC90ZXh0PgogIDwvc3ZnPg==" alt="Image Caching Visualization" /></p> <h2 id="getting-started-with-cached_network_image">Getting Started with cached_network_image</h2> <h3 id="add-the-package">1. Add the Package</h3> <p>First, add the package to your <code>pubspec.yaml</code> file:</p> <pre>dependencies: flutter: sdk: flutter cached_network_image: ^3.2.3 </pre> <p>Run <code>flutter pub get</code> to install the package.</p> <h3 id="basic-usage">2. Basic Usage</h3> <p>The simplest way to use the package is to replace your <code>Image.network</code> widgets with <code>CachedNetworkImage</code>:</p> <pre>import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart';
class BasicExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Cached Network Image'), ), body: Center( child: CachedNetworkImage( imageUrl: "https://picsum.photos/id/237/200/300", placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), ), ), ); } } </pre> <p>This will:</p> <ul> <li>Display a loading spinner while the image loads</li> <li>Show the image once downloaded (and cache it)</li> <li>Show an error icon if the image fails to load</li> </ul> <h2 id="customizing-cachednetworkimage">Customizing CachedNetworkImage</h2> <h3 id="adding-loading-placeholder">Adding Loading Placeholder</h3> <p>You can customize the placeholder widget shown during loading:</p> <pre>CachedNetworkImage( imageUrl: "https://picsum.photos/id/237/200/300", placeholder: (context, url) => Container( color: Colors.grey[300], child: Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation<Color>(Colors.blue), ), ), ), errorWidget: (context, url, error) => Icon( Icons.error, color: Colors.red, size: 30, ), ) </pre> <h3 id="configuring-image-properties">Configuring Image Properties</h3> <p>You can use most of the same properties available with regular Image widgets:</p> <pre>CachedNetworkImage( imageUrl: "https://picsum.photos/id/237/200/300", imageBuilder: (context, imageProvider) => Container( height: 200, width: 200, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), image: DecorationImage( image: imageProvider, fit: BoxFit.cover, ), ), ), placeholder: (context, url) => Container( height: 200, width: 200, child: Center( child: CircularProgressIndicator(), ), ), errorWidget: (context, url, error) => Icon(Icons.error), ) </pre> <h3 id="using-fade-in-animation">Using Fade-In Animation</h3> <p>You can animate the appearance of images as they load:</p> <pre>CachedNetworkImage( imageUrl: "https://picsum.photos/id/237/200/300", placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), fadeInDuration: Duration(milliseconds: 500), fadeInCurve: Curves.easeIn, ) </pre> <h2 id="advanced-configuration">Advanced Configuration</h2> <h3 id="configuring-cache-settings">Configuring Cache Settings</h3> <p>You can configure the caching behavior by using the <code>CacheManager</code>:</p> <pre>import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
// Create a custom cache manager class CustomCacheManager { static const key = 'customCacheKey'; static CacheManager instance = CacheManager( Config( key, stalePeriod: Duration(days: 7), maxNrOfCacheObjects: 100, repo: JsonCacheInfoRepository(databaseName: key), fileService: HttpFileService(), ), ); }
// Use the custom cache manager CachedNetworkImage( imageUrl: "https://picsum.photos/id/237/200/300", cacheManager: CustomCacheManager.instance, placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), ) </pre> <h3 id="handling-image-progress">Handling Image Progress</h3> <p>You can track the download progress of an image:</p> <pre>CachedNetworkImage( imageUrl: "https://picsum.photos/id/237/200/300", progressIndicatorBuilder: (context, url, downloadProgress) => CircularProgressIndicator(value: downloadProgress.progress), errorWidget: (context, url, error) => Icon(Icons.error), ) </pre> <h2 id="example-image-gallery-with-caching">Example: Image Gallery with Caching</h2> <p>Here's a complete example of an image gallery that uses cached network images:</p> <pre>import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart';
class ImageGallery extends StatelessWidget { final List<String> imageUrls = [ "https://picsum.photos/id/1001/800/800", "https://picsum.photos/id/1002/800/800", "https://picsum.photos/id/1003/800/800", "https://picsum.photos/id/1004/800/800", "https://picsum.photos/id/1005/800/800", "https://picsum.photos/id/1006/800/800", ];
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Cached Image Gallery'), ), body: GridView.builder( padding: EdgeInsets.all(8), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: imageUrls.length, itemBuilder: (context, index) { return ClipRRect( borderRadius: BorderRadius.circular(12), child: CachedNetworkImage( imageUrl: imageUrls[index], fit: BoxFit.cover, placeholder: (context, url) => Container( color: Colors.grey[300], child: Center( child: CircularProgressIndicator(), ), ), errorWidget: (context, url, error) => Container( color: Colors.grey[300], child: Icon( Icons.error, color: Colors.red, ), ), ), ); }, ), ); } } </pre> <h2 id="clearing-the-cache">Clearing the Cache</h2> <p>Sometimes you might need to clear the image cache:</p> <pre>// Clear a specific image cache await DefaultCacheManager().removeFile("https://picsum.photos/id/237/200/300");
// Clear all image caches await DefaultCacheManager().emptyCache(); </pre> <h2 id="performance-tips-and-best-practices">Performance Tips and Best Practices</h2> <h3 id="specify-image-dimensions">1. Specify Image Dimensions</h3> <p>Always specify image dimensions when possible to avoid layout shifts:</p> <pre>CachedNetworkImage( imageUrl: "https://picsum.photos/id/237/200/300", width: 200, height: 300, fit: BoxFit.cover, placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), ) </pre> <h3 id="use-memcachewidth-and-memcacheheight">2. Use memCacheWidth and memCacheHeight</h3> <p>Reduce memory usage by specifying the cached image dimensions:</p> <pre>CachedNetworkImage( imageUrl: "https://picsum.photos/id/237/1000/1500", memCacheWidth: 400, // Resize the image in memory to 400 pixels wide memCacheHeight: 600, // Resize the image in memory to 600 pixels high placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), ) </pre> <h3 id="handle-different-screen-sizes">3. Handle Different Screen Sizes</h3> <p>Fetch images appropriate to screen size:</p> <pre>CachedNetworkImage( imageUrl: MediaQuery.of(context).size.width > 800 ? "https://picsum.photos/id/237/1000/1000" // High-res : "https://picsum.photos/id/237/500/500", // Low-res placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), ) </pre> <h3 id="clear-old-caches-for-long-lived-apps">4. Clear Old Caches for Long-lived Apps</h3> <p>For long-lived apps, consider clearing old caches periodically:</p> <pre>void clearOldCache() async { await DefaultCacheManager().emptyCache(); print('Cache cleared!'); }
// Call this method when appropriate, e.g., on app start @override void initState() { super.initState();
// Check the last cache clear date and clear if older than a week final lastClearDate = await getLastClearDate(); final oneWeek = Duration(days: 7);
if (DateTime.now().difference(lastClearDate) > oneWeek) { clearOldCache(); await saveLastClearDate(DateTime.now()); } } </pre> <h2 id="troubleshooting-common-issues">Troubleshooting Common Issues</h2> <h3 id="images-not-caching">Images not caching</h3> <ul> <li>Check if you have sufficient storage permissions</li> <li>Verify the URLs are accessible and consistent</li> <li>Make sure the device has enough storage space</li> </ul> <h3 id="memory-issues-with-large-images">Memory issues with large images</h3> <ul> <li>Use <code>memCacheWidth</code> and <code>memCacheHeight</code> parameters</li> <li>Consider using placeholder images that match the expected size</li> <li>Load lower resolution images when appropriate</li> </ul> <h3 id="images-not-showing-offline">Images not showing offline</h3> <ul> <li>Ensure the images were successfully cached during online access</li> <li>Check if the cache has been cleared</li> <li>Verify the cache duration settings</li> </ul> <h2 id="conclusion">Conclusion</h2> <p>Implementing image caching in Flutter apps is straightforward with the <code>cached_network_image</code> package. This approach significantly improves user experience by reducing loading times, minimizing data usage, and enabling offline access to previously viewed images.</p> <p>By following the best practices outlined in this guide, you can ensure your app handles images efficiently and provides a smooth experience for your users.</p> <p>Happy coding!</p>