DEV Community

Cover image for ListView.builder vs ListView: Is There a Difference?
Gustavo Guedes
Gustavo Guedes

Posted on

ListView.builder vs ListView: Is There a Difference?

Recently, I came across a LinkedIn post where the author mentioned that during interviews with candidates who claimed to be Senior, questions at a Junior or even lower level were not being answered satisfactorily.

This inspired me to create a series of articles to explore and clarify these topics, aiming to consolidate my knowledge and help the community answer these questions more confidently.

In this article, we'll discuss the difference between ListView.builder and the default ListView constructor.

What are ListView and ListView.builder?

According to the Flutter documentation, ListView is "a scrollable list of widgets arranged linearly."

On the other hand, ListView.builder: "creates a scrollable, linear array of widgets that are generated on demand. This constructor is suitable for list views with a large (or infinite) number of children because the builder is only called for those children that are actually visible."

The key difference lies in how the widgets are created and managed.

A simple example of using a ListView is:

ListView(
  children: [
    ListTile(title: Text('Item 1')),
    ListTile(title: Text('Item 2')),
    ListTile(title: Text('Item 3')),
  ],
);
Enter fullscreen mode Exit fullscreen mode

This is a simple way to enable scrolling on a list of items. A similar approach would be:

SingleChildScrollView(
  child: Column(
    children: [
      ListTile(title: Text('Item 1')),
      ListTile(title: Text('Item 2')),
      ListTile(title: Text('Item 3')),
    ],
  )
);
Enter fullscreen mode Exit fullscreen mode

The behavior would be the same.

For simple lists where the number of items is small and all need to be loaded at once, the default ListView is a good option.

Now, imagine a scenario where the component displaying the items triggers an event for the Analytics service every time an item is created. If we use ListView.builder, some items that are never created won't trigger this event, potentially leading to incomplete data in the Analytics service.

But what about ListView.builder? What does "widgets created on demand" mean? Let's dive deeper into this concept.

On-Demand Rendering

On-demand rendering is a widely used concept, not only in Flutter but in many other technologies, such as:

List - SwiftUI
RecyclerView - Android (Java/Kotlin)
LazyColumn - Jetpack Compose (Android/Kotlin)
UITableView - UIKit (iOS - Swift)
FlatList - React Native
VirtualScroller - Vue.js
react-window or react-virtualized - React.js

Nearly every modern technology implements this concept. When dealing with lists containing hundreds or thousands of items, loading everything into memory at once can lead to performance issues, freezing, or even crashes.

The idea is simple: only load into memory what is currently being displayed, along with a few items before and after the visible area, if needed.

For example, imagine a list of 100 items, and the user is viewing items 10 through 15. The technology will load items around that range, such as from positions 5 to 20. This calculation depends on factors like item size, scroll position, and screen size.

A Closer Look at ListView.builder

One important aspect of ListView.builder is the itemCount property. Although optional, it helps optimize the rendering process. If you cannot define itemCount, the itemBuilder will handle notifying the parent ListView.builder when there are no more items to render.

The typing of this itemBuilder is: NullableIndexedWidgetBuilder, which in turn is:

NullableIndexedWidgetBuilder = Widget? Function(
  BuildContext context,
  int index
)
Enter fullscreen mode Exit fullscreen mode

If at any point itemBuilder returns null, this will stop the creation of further items, even if itemCount is set.

Example:

ListView.builder(
  itemCount: 100,
  itemBuilder: (context, index) {
    print(index);
    return ListTile(
      title: Text("Item $index"),
    );
  },
),
Enter fullscreen mode Exit fullscreen mode

Using an iPhone 15 Pro Max simulator, the console printed me from this list up to index 19, so out of 100 items only 20 of them are in memory. And if we make the following edit:

ListView.builder(
  itemCount: 100,
  itemBuilder: (context, index) {
    if (index == 50) {
      return null;
    }

    print(index);
    return ListTile(
      title: Text("Item $index"),
    );
  },
),
Enter fullscreen mode Exit fullscreen mode

The display will display up to element 50, index 49.

gif

Stay tuned!

Therefore, understanding how these properties work together is crucial to avoid inconsistencies in list rendering.

Conclusion

Using ListView.builder is generally recommended to reduce memory usage, especially on older devices. However, we’ve seen that there are scenarios where the traditional ListView may be a better fit.

There is still much more to explore, such as ListView.custom, ListView.separated, and integrations with SliverChildBuilder. But with the concepts discussed here, you should now be able to understand the main differences between these two widgets and when to use them.

Top comments (0)