Flutter ListView.builder: Efficient, Dynamic Scrolling Lists

Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter ListView.builder guide, with lazy item building, itemBuilder, separators, and itemExtent performance visuals.
Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter ListView.builder guide.

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 course thumbnail

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 now

Every 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.

Follow me on Instagram@sagnikteaches

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.

Connect on LinkedInSagnik Bhattacharya

If you'd rather watch an infinite feed built and profiled, the channel covers list performance inside real, data-driven apps.

Subscribe on YouTube@codingliquids

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.

The Complete Flutter Guide course thumbnail

Lists that stay smooth at scale

The Complete Flutter Guide covers list building, performance, and pagination from first principles through to shipped apps.

Enrol now

The 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 Expanded so the list keeps its lazy building.
  • Dividers inside each item. Use ListView.separated for 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:

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.