Flutter ListView.builder: The Complete Beginner’s Guide (2026)

Every Flutter app with more than a few items needs a list. The question is which list widget to use โ€” and getting this wrong on a large dataset means your app builds hundreds of widgets at once, eating memory and causing jank.

Flutter’s answer is ListView.builder: a lazy-loading list that only builds the items currently visible on screen. It is the official recommendation for any long, dynamic, or API-driven list, and once you understand its two required parameters, almost every list in your app becomes easy to build.

This guide starts with the minimal example and builds up to real-world patterns: lists of objects, custom card widgets, dividers, pull-to-refresh, loading skeletons, basic pagination, and horizontal lists. Every common mistake has a working fix.

๐Ÿ“‹ Prerequisites

1. ListView vs ListView.builder

Flutter has two primary list constructors. Understanding the difference is the first thing to get right before writing any list code:

Feature ListView ListView.builder
Renders itemsAll at once on first buildOnly visible items, lazily on demand
Memory useHigher โ€” all widgets exist at onceEfficient โ€” recycles off-screen widgets
Best forShort, fixed lists (โ‰ค ~20 items)Long, dynamic, or API-driven lists
Requires itemCount?โŒ No โ€” pass children directlyโœ… Strongly recommended
Requires itemBuilder?โŒ Noโœ… Yes โ€” required parameter
๐Ÿ’ก Simple rule
If your list comes from a variable (List<String>, API response, database query) โ€” use ListView.builder. If you are hardcoding 3โ€“5 widgets โ€” ListView with a children: list is fine. When in doubt, use ListView.builder.

2. The Minimal Example

ListView.builder needs exactly two things: itemCount (how many items total) and itemBuilder (what to render for each index). The builder receives two arguments โ€” BuildContext context and int index โ€” where index starts at 0 and goes up to itemCount - 1:

โš ๏ธ Warning: Always set itemCount for data-driven lists
If you omit itemCount, Flutter treats the list as infinite โ€” itemBuilder is called forever with incrementing indexes. This can cause index-out-of-range crashes when your builder tries to access myList[index] beyond the list bounds, and creates unnecessary blank space below the real items.

3. Real Example with a Data List

Most apps render ListView.builder from a real List. The key is passing myList.length as itemCount and accessing myList[index] inside itemBuilder. Here is a complete example with a list of maps:

4. Custom Card Item Widget

As list items get more complex, keeping all the UI inside itemBuilder makes it hard to read and maintain. Extract each item into its own widget. This keeps itemBuilder to a single line and lets Flutter optimise rebuilds more precisely:

5. ListView.separated โ€” Dividers and Gaps

ListView.separated is a variant that adds a separator widget between each item. It requires one extra parameter โ€” separatorBuilder โ€” and renders it between every pair of adjacent items (so 10 items get 9 separators):

6. Stateful Lists โ€” Adding and Removing Items

When the list changes โ€” items added, deleted, or reordered โ€” the widget must be StatefulWidget and every mutation must happen inside setState(). Here is a complete example with add and swipe-to-delete:

7. Pull-to-Refresh with RefreshIndicator

Wrapping ListView.builder in a RefreshIndicator adds the standard pull-to-refresh gesture. The onRefresh callback must return a Future โ€” the spinner shows until the future completes:

๐Ÿ’ก Tip: AlwaysScrollableScrollPhysics is required
If the list has fewer items than the screen height, it won’t scroll by default โ€” which means RefreshIndicator‘s pull gesture won’t trigger. Set physics: const AlwaysScrollableScrollPhysics() to ensure the pull gesture always works, even on short lists.

8. Loading State and Empty State

Real apps fetch data asynchronously. The list needs to handle three states: loading (show a spinner or skeleton), empty (show a helpful message), and loaded (show the list). Here is the full pattern:

9. Basic Pagination โ€” Load More on Scroll

For large datasets, loading all items at once wastes bandwidth. Detect when the user scrolls near the bottom using a ScrollController, then fetch the next page:

10. Horizontal Lists and Grid Alternatives

ListView.builder scrolls vertically by default. Set scrollDirection: Axis.horizontal for a horizontal list โ€” common for category chips, featured cards, or image carousels:

11. shrinkWrap and Nested Lists

The most common layout error with ListView.builder is putting it inside a Column without constraining its height. There are two fixes โ€” choose based on your use case:

โš ๏ธ Warning: shrinkWrap disables lazy loading
When shrinkWrap: true, Flutter must build ALL items at once to know the total height. This cancels the entire performance benefit of ListView.builder. Only use shrinkWrap for short, fixed lists (less than ~20 items). For any longer list, use Expanded instead.

12. Common Beginner Mistakes

Mistake 1: Missing itemCount โ€” infinite list and index crash

Mistake 2: ListView inside Column without Expanded

Mistake 3: Mutating the list without setState โ€” UI never updates

Mistake 4: Missing key on Dismissible โ€” wrong items deleted

Mistake 5: Hardcoding index instead of using the builder parameter

13. Interview Q&A

Q: What is ListView.builder in Flutter?

A: It is a ListView constructor that builds list items lazily โ€” only when they are about to scroll into view. Flutter calls itemBuilder on demand for visible items only and recycles widgets that scroll off screen. This makes it the correct choice for long, dynamic, or API-driven lists where building all items at once would waste memory and slow down the app.
Q: What is the difference between ListView and ListView.builder?

A: ListView renders all its children at once on first build โ€” fine for 5โ€“20 static items. ListView.builder only renders visible items on demand and recycles them as the user scrolls โ€” essential for 50+ items or any dynamic list. Think of ListView as printing every page of a book up front, versus ListView.builder printing only the page you are currently reading.
Q: What are the required parameters of ListView.builder?

A: Only itemBuilder is strictly required by the API โ€” it is a function that receives (BuildContext context, int index) and returns a widget. itemCount is not technically required, but should always be set for data-driven lists. Without it, Flutter treats the list as infinite and itemBuilder will be called beyond your list’s bounds, causing a RangeError crash.
Q: Why does Flutter throw “Vertical viewport was given unbounded height”?

A: Because ListView.builder inside a Column has no bounded height constraint โ€” the column gives it infinite vertical space and the list doesn’t know how tall to be. Fix it with Expanded (preferred โ€” list fills remaining space) or shrinkWrap: true with NeverScrollableScrollPhysics() (use only for short embedded lists, as it disables lazy loading).
Q: What does shrinkWrap: true do and when should you avoid it?

A: shrinkWrap: true makes the ListView size itself to its total content height instead of filling all available space. This is useful for short lists embedded inside a Column or SingleChildScrollView. However, it forces Flutter to build ALL items immediately to calculate the total height, which disables the lazy-loading behaviour that makes ListView.builder performant. Avoid it for lists longer than ~20 items โ€” use Expanded instead.
Q: What is ListView.separated?

A: It is a variant of ListView.builder that renders a separator widget between each item. It requires a separatorBuilder parameter alongside itemBuilder and itemCount. The separator can be a Divider, a SizedBox for spacing, or any widget. For 10 items, it renders 9 separators โ€” one between each adjacent pair.
Post Why it’s relevant
Flutter Widgets Explained: Stateless vs StatefulStateful lists (add, remove, refresh) depend entirely on StatefulWidget and setState() โ€” this is the prerequisite.
Flutter Layout Made Easy: Row, Column, Flex and ExpandedExplains Expanded โ€” the fix for the most common ListView.builder layout error.
Flutter Search Bar from ScratchBuilds directly on ListView.builder โ€” add real-time filtering to any list built here.
Handling User Input: TextField, Forms and ValidationThe add-item TextField in the stateful list section uses these exact patterns.
Building a Notes App โ€“ Part 1A real project that puts ListView.builder, Dismissible, and setState together in a working app.
Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply