Scrolling lists are the backbone of most apps, and how you build them decides whether they stay smooth at ten items or ten thousand. The plain ListView builds everything at once; ListView.builder builds items lazily, only as they scroll into view, which is what keeps long lists fast and memory-light. This guide covers the builder, separators, the performance levers, the shrinkWrap trap, and when each form is the right choice, with paste-ready code.

The Complete Flutter Guide: Build Android, iOS and Web apps
Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.
Enrol nowEvery snippet below is paste-ready against current stable Flutter. ListView.builder is the entry point to Flutter's scrolling model; the scrolling and slivers guide goes deeper, and Card and ListTile give you the items to put inside it.
We'll build a basic ListView.builder, add separators with ListView.separated, look at the performance levers like itemExtent, unpack the shrinkWrap trap, and settle when to use which list form.
If you'd rather watch an infinite feed built and profiled, the channel covers list performance inside real, data-driven apps.
The basic builder
ListView.builder takes an itemCount and an itemBuilder callback that returns the widget for a given index. Flutter calls the builder only for items near the viewport, so a list of 10,000 rows costs about the same to render as a list of ten.
ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) {
final m = messages[index];
return ListTile(
title: Text(m.sender),
subtitle: Text(m.preview),
);
},
)
This is the default you should reach for whenever the item count is large, unknown, or driven by data. The builder pattern is what makes the difference between a list that scrolls at 120fps and one that janks as it grows.
Separators between items
For dividers between rows, don't add them inside each item — use ListView.separated, which adds a separatorBuilder that runs between each pair of items and never after the last.
ListView.separated(
itemCount: contacts.length,
itemBuilder: (context, i) => ListTile(title: Text(contacts[i].name)),
separatorBuilder: (context, i) => const Divider(height: 1),
)
This keeps the item builder focused on the item and guarantees consistent spacing with no trailing divider — a cleaner result than baking a Divider into every row yourself.
Performance levers
Lazy building is most of the battle, but two settings help further when every row is the same height. Setting itemExtent (or providing a prototypeItem) lets Flutter skip measuring each child, which makes scrolling and jumping to an offset noticeably smoother.
ListView.builder(
itemExtent: 72, // every row is exactly 72px tall
itemCount: rows.length,
itemBuilder: (context, i) => RowTile(rows[i]),
)
Beyond that, keep item widgets light and const where you can, decode any images at display size with cacheWidth (see the Image guide), and give items stable keys when the list can reorder. Together these keep a long, image-rich list at a high frame rate.

Lists that stay smooth at scale
The Complete Flutter Guide covers list building, performance, and pagination from first principles through to shipped apps.
Enrol nowThe shrinkWrap trap
A common need is a list inside a Column. Because the column gives the list unbounded height, the obvious-looking fix is shrinkWrap: true — but that forces the list to measure all its children to size itself, throwing away the lazy building that makes the builder fast.
// Prefer this inside a Column:
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, i) => ItemTile(items[i]),
),
)
Wrapping the list in Expanded gives it a bounded height and keeps it lazy. Reserve shrinkWrap: true (usually with NeverScrollableScrollPhysics) for genuinely short lists that shouldn't scroll on their own, such as a few rows nested in a larger scroll view.
ListView vs ListView.builder
The plain ListView(children: [...]) constructor is fine — and slightly more readable — for a short, fixed set of widgets you know at build time, like five settings rows. The moment the list is long, data-driven, or potentially infinite, switch to ListView.builder. When in doubt, the builder is the safer default; it never performs worse than the eager constructor on a long list.
Common mistakes
- Using ListView(children: ...) for a long list. It builds everything up front; use
ListView.builder. - shrinkWrap inside a Column. Prefer
Expandedso the list keeps its lazy building. - Dividers inside each item. Use
ListView.separatedfor clean, trailing-free separators. - No itemExtent on fixed-height rows. Setting it lets Flutter skip per-item measurement.
- Missing keys on reorderable lists. Give items stable keys so Flutter tracks them across changes.
Frequently asked questions
What is the difference between ListView and ListView.builder in Flutter?
The default constructor builds all children eagerly; ListView.builder builds items lazily as they scroll into view. Use builder for anything beyond a short, fixed list.
How do I add separators between items in a Flutter list?
Use ListView.separated with a separatorBuilder; it inserts a separator between items and never after the last one.
Why should I avoid shrinkWrap on a Flutter ListView?
It measures all children to size the list, defeating lazy building. Inside a Column, wrap the list in Expanded instead.
How do I improve ListView.builder performance in Flutter?
Set itemExtent for fixed-height rows, keep items light and const, decode images at display size, and give reorderable items stable keys.
Further reads
Keep going with the tutorials that pair with this guide:
- Flutter Development Guide 2026 — the full Flutter hub.
- Flutter Scrolling and Slivers — the full scrolling model, from ListView to CustomScrollView.
- Flutter Card and ListTile — the items you put in each row.
- Flutter GridView Tutorial — the same builder pattern in two dimensions.
- Flutter Image Widget — cache images at display size for image-rich lists.
Sources: Flutter documentation — ListView, ListView.builder, ListView.separated, itemExtent, prototypeItem, and ScrollPhysics API references, plus the Lists and performance guides (docs.flutter.dev). Verified against current stable Flutter.