Chapter 3: Taming Long Lists - ListView vs. ListView.builder
Of course. After optimizing individual widgets, we'll tackle one of the most common bottlenecks in Flutter: displaying a long list.
The Scenario 📝
- System: A news application that needs to display a list of 500 articles.
- Problem: When the user opens the list screen, the app "freezes" for a long time before displaying anything. The application's memory (RAM) usage spikes.
The Problematic Code (Building the Entire List)
The most intuitive approach is to create a List<Widget> and pass it to a ListView.
dart
// Assume `allArticles` is a List containing 500 Article objects
class ArticleListScreen extends StatelessWidget {
final List<Article> allArticles;
ArticleListScreen({required this.allArticles});
@override
Widget build(BuildContext context) {
return ListView(
// MISTAKE: .map().toList() creates all 500 widgets at once
children: allArticles.map((article) {
return ArticleListItem(article: article);
}).toList(),
);
}
}The Bottleneck Analysis 🧐
- Build All at Once: The default
ListViewconstructor requires you to provide a completeList<Widget>. This means that before the screen can be displayed, Flutter must run themapfunction and create all 500ArticleListItemwidgets and load them into memory. - The Consequences:
- Very Slow Initial Load Time: The app will hang while it's busy building 500 widgets.
- High Memory Consumption: All 500 widgets, whether they are visible on the screen or not, occupy RAM.
- Poor User Experience: The user has to wait and might think the app has crashed.
The Solution: Use ListView.builder for "Lazy" Building ✅
The Logic:
ListView.builderis another constructor forListView, specifically designed for long lists. It does not build all the widgets at once. Instead, it only builds the widgets that are currently or about to be visible on the screen.Optimized Code:
dartclass ArticleListScreen extends StatelessWidget { final List<Article> allArticles; ArticleListScreen({required this.allArticles}); @override Widget build(BuildContext context) { return ListView.builder( itemCount: allArticles.length, // Provide the total number of items itemBuilder: (context, index) { // This function is only called for visible items // For example, it might only be called 10 times for the first 10 items print("Building item $index"); final article = allArticles[index]; return ArticleListItem(article: article); }, ); } }
Analysis of the Result ✨
- Instant Initial Load: The screen displays almost immediately because Flutter only needs to build the first 10-15 widgets to fill the screen.
- Memory Efficiency: Only the widgets currently on screen are kept in memory. As the user scrolls, old widgets that scroll off-screen are destroyed, and new ones are built.
- Smooth Scrolling: Because the framework only has to manage a small number of widgets at any given time, scrolling performance is very high.
Conclusion:
- The Golden Rule: Always use
ListView.builder(or similar builders likeGridView.builder,CustomScrollView) when you have a long list or a list of unknown length. - The default
ListViewconstructor should only be used for very short lists where all items can be displayed on the screen at once (e.g., a settings menu).