Chapter 4: The Jank-inator - Optimizing Network Images β
Of course. After optimizing the building of widgets in a list, we'll now optimize the content within them, specifically the thing that causes the most "jank": loading images from the network.
The Scenario π β
- System: A shopping app that displays a list of products using
ListView.builder. Each item in the list has a product image loaded from a URL. - Problem: When the user scrolls the list, the app "janks" or stutters every time a new image appears. The image also appears abruptly after it's finished loading, and if you scroll away and then back, the image has to be downloaded all over again.
The Problematic Code (Using Image.network) β
The simplest approach is to use the built-in Image.network widget.
dart
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
child: Column(
children: [
// MISTAKE: Using Image.network directly in a long list
Image.network(product.imageUrl),
Text(product.name),
],
),
);
},
);The Bottleneck Analysis π§ β
- No Caching: The
Image.networkwidget is very basic. Every time it's built, it will always send a network request to download the image, even if that image has been downloaded before. If the user scrolls a widget off-screen and then back on, the widget is rebuilt, and the image is downloaded all over again. - No Loading Indicator: While the image is being downloaded, the widget just shows an empty space. The user doesn't know what's happening.
- No Error Handling: If the image fails to load (e.g., weak network, broken URL), the user will just see a cryptic error.
The Solution: Use a Specialized Package like cached_network_image β
β
The Logic: Instead of handling the complex logic of caching, showing loading indicators, and handling errors yourself, use a powerful community library that has already done it for you.
cached_network_imageis the most popular and best choice.Optimized Code:
- Add the package to
pubspec.yaml:cached_network_image: ^3.3.1 - Use the
CachedNetworkImagewidget:
dartimport 'package:cached_network_image/cached_network_image.dart'; ListView.builder( itemCount: products.length, itemBuilder: (context, index) { final product = products[index]; return Card( child: Column( children: [ // OPTIMIZED: Use CachedNetworkImage CachedNetworkImage( imageUrl: product.imageUrl, // Show a spinner while waiting placeholder: (context, url) => Center(child: CircularProgressIndicator()), // Show an error icon if it fails errorWidget: (context, url, error) => Icon(Icons.error), // Fade in the image for a smoother appearance fadeInDuration: const Duration(milliseconds: 300), ), Text(product.name), ], ), ); }, );- Add the package to
Analysis of the Result β¨ β
- Smooth Scrolling: After the first download, images are saved to the cache (both in memory and on disk). When the user scrolls back, the image is loaded from the cache almost instantly, eliminating network latency and making scrolling much smoother.
- Better User Experience:
- The
placeholderprovides immediate feedback, letting the user know the app is loading data. - The
errorWidgethandles error cases gracefully.
- The
- Saves Network Data: The app doesn't have to re-download unnecessary images, which saves data for the user.
Conclusion:
- The Golden Rule: "Never use
Image.networkdirectly in a long, scrollable list." - Always use a specialized package like
cached_network_imageto manage loading and caching network images. It provides the essential features needed to create a professional and high-performance application.