ListView: how to handle lists in Flutter

Creating a list using Flutter is really easy as it comes with a built-in widget called ListView, which handles everything for you. Let’s see how to make a basic usage of this widget.

To render a list, add a ListView widget. It has many properties, and one of these is called children, which takes an array of widgets as value. So, a simple list would look like this:

All the elements of the list should be defined inside the children property of the ListView.

This creates a scrollable list with the widgets passed in the children property.

Space rules

There are two rules you need to know about to understand how ListView decides how much space to take on your screen.

  1. the list will fill up all the width available;
  2. the height of each item should be defined, implicitly, or explicitly.

There’s not much to say about the first point: unless you have a widget with a defined width to contain your list, it will fill up all the available space horizontally.

The second rule means that when you define an item of the list, the ListView should be able to tell how high each item is going to be.

Usually, you don’t really need to put thoughts into this last point, as the height can easily be calculated by putting together paddings, font sizes, etc. The real problem relies on the missing definition of width, as we’re going to see later in the article.

ListTile: a useful widget for list items

Another useful widget to know about is the ListTile widget. This is usually used to render each list item as it is flexible enough for that use. It has a title, a subtitle, a leading widget, and a trailing widget, as you can see in the pictures below.

The result looks like this:

This widget is great if you want your list elements to comply with the material design specifications. It has two properties to enable interaction: the onTap and the onLongPress callbacks.

It has more to offer; you can read about this widget by heading to ListTile class.

The builder constructor: the performance issue

The problem

We’ve seen how to implement a list in Flutter. The method showed before is good if you have a list with few items, but it suffers from performance issues if you have to handle a longer list: this happens because it tries to render each item of the list, even though they are not visible on the screen! You understand that for very long lists, it can be a problem.

The solution

To solve the mentioned problem, ListView comes with a special constructor called builder, and it is intelligent enough to render only the widgets currently visible on the screen. A list using the builder constructor looks like this:

The first thing to notice is that you won’t use the “children” property anymore by using the builder constructor. Instead, you have to define the itemCount and the itemBuilder property; let’s see what they are.

itemCount and itemBuilder explained

The first property is called itemCount. This should be valued with the length of the list or, in other words, the number of elements in the list. As we’re going to see, this is needed because using the builder constructor, the ListView can’t infer the list length from the number of the children as it did in the previous example.

The second parameter is named itemBuilder. This takes as value a function that should return a widget. The function takes the context and an integer index as value, and it should return the widget that has to be rendered in the index-th position in the list. A real case example would look something like this:

In the example above, the itemCount property is exactly the length of the items array, and the itemBuilder takes a function that returns the index-th string in the items array wrapped in a Text widget.

The behavior of lists when itemCount is missing.

You should note that itemCount is not a required parameter, so it can happen you forget to add it. The list will behave normally initially, but Flutter will warn you as soon as you scroll past the last item of the list. Given that itemCount was not added, the ListView can’t tell the list length, causing the scroll not to be bounded.

The separated constructor and the Divider widget

We’ve already seen two constructors of the ListView widget. It has a third one called separated. This one is built upon the previous one and adds a new feature: you can put some divider between your list items.

There are many ways of dividing your content. For example, a straight line may be used, or 3 horizontal dots, as Medium itself does. Flutter has the Divider widget that draws a horizontal line.

Let’s see what we need to use this constructor:

The code above will generate the following list:

What changed from using the builder constructor to using the separated one is the separatorBuilder parameter.

The separatorBuilder parameter explained.

The separatorBuilder works exactly as the itemBuilder. Instead of handling the list elements, it takes care of each separator: you need to pass in a function that takes a BuilderContext and an integer index, and it should return a widget.

It doesn’t happen much often to the function’s inputs to be of any need. That’s because usually, the separator should always be the same, no matter which items of the list you’re dividing. That said, if the one above isn’t the case, you can use the context and the index to define which widget to return as we do with the itemBuilder parameter.

How to implement a horizontal list

Sometimes you want to swipe through your items horizontally. In Flutter, it’s straightforward to achieve that!

All you need to do is define the property scrollDirection, which should be assigned a value from the Axis enum, and one of its values is horizontal, the one we were looking for. Let’s see how it all comes together:

You may have noticed that each item has a specified width. That’s because horizontal lists behave the exact opposite of a vertical one. The rules applied to a vertical list we’ve seen before don’t work here anymore. So, let’s see what changed.

Space rules

As we said, a horizontal list behaves the exact opposite to a vertical one. A ListView consumes space using the following rules:

  1. the list will fill up all the height available;
  2. the width of each item should be defined, implicitly, or explicitly.

In the example above, if we didn’t add a width, nothing would appear on the screen, as the ListView wouldn’t know how large each item should be because there’s no information about it anywhere. There’s no way to compute it by putting together other information like font size, paddings, margins, etc.

Because of the first rule, instead, the list will fill up the whole screen unless the MyList widget is rendered inside another widget with a defined height.

TLDR: takeaways

To sum up the content of this article:

  • use the ListView’s base constructor in case you’re going to render a list with few items;
  • use the ListTile widget if you want the list elements to comply with the material design specifications, or you’re looking for some basic styling for your items;
  • use the builder constructor of ListView in case you have a long list as it has some performance optimization in place: it renders only the items actually visible on the screen;
  • use the separated constructor if you want an easy way to put some divider between your list items. It also has the advantages of the builder constructor;
  • To implement a horizontal list, all you need to do is set the scrollDirection property of ScrollView to Axis.horizontal, remember that the items need to have a defined width.

Final words

That’s all. There is plenty more Flutter has to offer regarding lists, but I wanted to lay down the most useful concepts.

There may be a second part to this article with some more advanced notions, yet pretty useful ones.

Reach out for any questions or suggestions.

Thanks for reading.

--

--

Taranjit Singh

Graduated in computer science | working as fullstack developer