Pinkesh Darji I love to solve problems using technology that improves users' lives on a major scale. Over the last seven-plus years, I've been developing and leading various mobile apps in different areas.

How to add ListTile in Flutter: A tutorial with examples

6 min read 1766

How to add ListTile in Flutter: A tutorial with examples

Most of the time when developing, you might find yourself populating the ListView with some kind of predefined format. Instead of creating this layout by yourself using Rows, Columns, and Containers, you can use the ready-made widget in Flutter called the ListTile widget to help.

In this tutorial, I will show you how to add a ListTile widget in a Flutter app by walking through some practical examples.

Here’s what we’ll cover today:

What is ListTile?

The ListTile widget in Flutter is a UI element that displays related information.

It follows the List specification from Material Design. A typical ListTile is divided into three sections; Start, Center, and End. The Start section contains the leading widget; the Center section includes the title and subtitle; and the End section contains the trailing widget.

Diagram depicting list tile sections

It is mainly used to populate the scrollable views such as ListView, Column, and Row. For example, you can use the ListTile to show a list of to-do items, emails, navigation options, and more. You can also receive the click event by tapping the ListTile.

If you’re a visual learner, check out this quick video tutorial:

Adding ListTile

Here is the minimal code to display ListTile inside the ListView widget:



ListView(
  children: const [
    ListTile(
      leading: Icon(Icons.car_rental),
      title: Text('Car'),
      trailing: Icon(Icons.more_vert),
    ),
    ListTile(
      leading: Icon(Icons.flight),
      title: Text('Flight'),
      trailing: Icon(Icons.more_vert),
    ),
    ListTile(
      leading: Icon(Icons.train),
      title: Text('Train'),
      trailing: Icon(Icons.more_vert),
    )
  ],
)

Here’s how the code is translated into design:

List tile code with result showed on iPhone

When you want to use the ListTile to populate the long list that might come from your backend, you can wrap the single ListTile widget inside the ListView.Builder and show the data inside the ListTile, as shown in the following code:

final List<String> items = List<String>.generate(10000, (i) => '$i');
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      leading: CircleAvatar(
        backgroundColor: const Color(0xff764abc),
        child: Text(items[index]),
      ),
      title: Text('Item ${items[index]}'),
      subtitle: Text('Item description'),
      trailing: Icon(Icons.more_vert),
    );
  },
)

Output:

List tile items on iPhone

ListTile variations

There are also other types of ListTile which exist that allow you to perform a specific action on it.

These are:

  1. CheckboxListTile
  2. RadioListTile
  3. SwitchListTile

CheckboxListTile

The CheckboxListTile widget is a combination of ListTile and Checkbox widgets.

You can use this widget to mark any item as complete — for example; to-do items. By default, the checkbox is displayed on the right side of the ListTile (for left-to-right language).

Here’s how you can add the CheckboxListTile widget:

class Language {
  String name;
  bool isChecked;
  Language(this.name, {this.isChecked = false});
}
// 1.
final List<Language> languages = [Language('English'), Language('French'), Language('German')];
ListView.builder(
  itemCount: languages.length,
  itemBuilder: (context, index) {
    return CheckboxListTile(
      // 2.
      title: Text('${languages[index].name}'),
      // 3.
      value: languages[index].isChecked,
      // 4.
      onChanged: (bool? value) {
        setState(() {
          languages[index].isChecked = value!;
        });
      },
      // 5.
      secondary: const Icon(Icons.language),
    );
  },
)

Explanation of numbers in code block:

  1. A variable that holds a list of languages
  2. This shows the checkbox label
  3. This determines whether to check or uncheck the item
  4. This is called when you tap on the ListTile
  5. This acts as a leading icon

Output:

English, French and German with checkable boxes

To swap the secondary (leading) widget and checkbox, you can use the controlAffinity property and set it to ListTileControlAffinity.leading.


More great articles from LogRocket:


Checkboxes now on the left of the text

You can also change the shape of the checkbox by adding the checkboxShape parameter and setting it to RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)).

Circular checkboxes

RadioListTile

The RadioListTile widget is a combination of ListTile and RadioButton widgets — this widget is used to select a single option from a list of items.

Here’s how you can add the RadioListTile widget:

// 1.
enum Gender { male, female }
// 2.
Gender? _gender = Gender.male;
ListView(
  children: [
    // 3.
    RadioListTile<Gender>(
      secondary: Icon(Icons.male),
      controlAffinity: ListTileControlAffinity.trailing,
      title: const Text('Male'),
      // 4.
      value: Gender.male,
      // 5.
      groupValue: _gender,
      // 6.
      onChanged: (Gender? value) {
        setState(() {
          _gender = value;
        });
      },
    ),
    RadioListTile<Gender>(
      secondary: Icon(Icons.female),
      controlAffinity: ListTileControlAffinity.trailing,
      title: const Text('Female'),
      value: Gender.female,
      groupValue: _gender,
      onChanged: (Gender? value) {
        setState(() {
          _gender = value;
        });
      },
    ),
  ],
)

Explanation of numbers in code block:

  1. An enum that holds all selection values for RadioListTile
  2. This saves default selection using enum
  3. Adding RadioListTile of type enum
  4. Assigning selection value to the current list tile. The ListTile represents this value
  5. This is used to display the currently selected value
  6. This gets called with the selection when you toggle the radio button

Output:

male or female option

SwitchListTile

The SwitchListTile widget is a combination of the ListTile and Switch widgets.

You can use this widget to build the UI interaction that allows users to switch on or off app settings.

Here’s how you can add the SwitchListTile widget:

class Appliance {
  String name;
  bool isOn;
  Appliance(this.name, {this.isOn = false});
}
// 1.
final List<Appliance> appliances = [
  Appliance('TV'),
  Appliance('Fan'),
  Appliance('Refrigerator'),
];
ListView.builder(
  itemCount: appliances.length,
  itemBuilder: (context, index) {
    return SwitchListTile(
      // 2.
      title: Text('${appliances[index].name}'),
      // 3.
      value: appliances[index].isOn,
      // 4.
      onChanged: (bool value) {
        setState(() {
          appliances[index].isOn = value;
        });
      },
    );
  },
)

Explanation of numbers in code block:

  1. A variable that holds a list of appliances
  2. This shows the name or title of the ListTile
  3. This determines whether to turn the switch on or off
  4. This is called when you toggle the switch

Output:

List items with toggle switch

Managing ListTile theme

The theme is an essential aspect of developing a front-end app. The theme conveys your brand, and — if not managed carefully — you may be wasting a lot of your time making all the UI elements follow the same rule.

Let’s say you want to change the look and feel of the ListTile to match your design. You really have the following two options:

  1. Changing theme at the widget level
  2. Changing theme at the app level

Changing theme at the widget level

You can change the ListTile’s appearance by directly modifying its properties such as color, shape, and size.

Here’s how you can change the ListTile’s background color, text color, and icon color:

return ListTile(
  // 1.
  tileColor: Colors.redAccent,
  // 2.
  textColor: Colors.white,
  // 3.
  iconColor: Colors.white,
);

Explanation of numbers in code block:

  1. This changes the background color of the ListTile
  2. This changes the color of all the text that appears on ListTile
  3. This changes the color of all the icons that appear on ListTile

Changing theme at the app level

You would probably want to change the visual aesthetic of the ListTile widget across all app pages. You can do so by defining the listTileTheme and then adding the ListTileThemeData widget.

Inside the ListTileThemeData widget, you can specify all the properties that you would like to change for all the ListTile widgets in your project.

Here is the code example:

return MaterialApp(
  title: 'Flutter Demo',
  debugShowCheckedModeBanner: false,
  theme: ThemeData(
    primarySwatch: Colors.blue,
    listTileTheme: ListTileThemeData(
      tileColor: Colors.redAccent,
      textColor: Colors.white,
      iconColor: Colors.white,
    ),
  ),
  home: MyHomePage(),
);

Both the first and second approach produce the same result as in the following:

Background is now red

Adding a divider

Adding a divider helps you clearly distinguish between the items, especially when items are displayed with three lines in the center section.

To add the divider between ListTile items, add the ListView widget. Inside ListView, add the ListTile.divideTiles with the tiles property and a list of ListTiles.

Code Example:

ListView(
  children: ListTile.divideTiles(context: context, tiles: [
    ListTile(
      leading: Icon(Icons.car_rental),
      title: Text('Car'),
    ),
    ListTile(
      leading: Icon(Icons.flight),
      title: Text('Flight'),
    ),
    ListTile(
      leading: Icon(Icons.train),
      title: Text('Train'),
    ),
  ]).toList(),
)

Output:

List tile divider

If you are using ListView.Builder, you can replace it with the ListView.separated and add the separatorBuilder parameter that returns the divider.

Code example:

ListView.separated( // <-- SEE HERE
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      leading: CircleAvatar(
        backgroundColor: const Color(0xff764abc),
        child: Text(items[index]),
      ),
      title: Text('Item ${items[index]}'),
      subtitle: Text('Item description'),
      trailing: Icon(Icons.more_vert),
    );
  },
  separatorBuilder: (context, index) { // <-- SEE HERE
    return Divider();
  },
)

Output:

Adding swipe-to-dismiss behavior

The swipe-to-dismiss feature allows you to remove a particular ListTile from the list using a swipe gesture. For example, you could you this feature to remove an email from your list.

Here are the steps:

  1. Wrap your ListTile widget inside the Dismissible widget
  2. Inside the Dismissible widget, add the onDismissed parameter and register a callback. Here you can write the logic to remove the item from the list.
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return Dismissible( // Step 1
      key: Key(items[index]),
      onDismissed: (direction) { // Step 2
        setState(() {
          items.removeAt(index);
        });
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${items[index]} dismissed')));
      },
      child: ListTile(
        //visualDensity: VisualDensity(vertical: 4),
        leading: CircleAvatar(
          backgroundColor: const Color(0xff764abc),
          child: Text(items[index]),
        ),
        title: Text('Item ${items[index]}'),
        subtitle: Text('Item description'),
        trailing: Icon(Icons.more_vert),
      ),
    );
  },
)
)

(Note: The above code only removes the ListTile from the UI, and you’ll have to write the business logic yourself to remove the item from your database)

Output:

User swipes to remove list items

Changing ListTile height

Sometimes you might want to adjust the ListTile height up to some extent. The ListTile widget does not support the height property directly because its dimensions are constrained as per the Material design specification. So, in order to increase or decrease the ListTile height, you can use the visualDensityproperty.

Setting the visualDensity to a positive number will increase the ListTile height, whereas a negative number will decrease the height.

(Note: The maximum and minimum values you can set it to are 4 and -4)

Here is the code example:

ListTile(
  visualDensity: VisualDensity(vertical: 4), //<-- SEE HERE
  leading: CircleAvatar(
    backgroundColor: const Color(0xff764abc),
    child: Text(items[index]),
  ),
  title: Text('Item ${items[index]}'),
  subtitle: Text('Item description'),
  trailing: Icon(Icons.more_vert),
);

Output:

List item tiles have larger height

Customization

You can add various customizations to the ListTile by utilizing the available properties. For example, you can change its color (on different states such as hover, pressed, etc.), shape, add space between the title and other elements, and change its height, etc.

You can see all properties it supports by navigating to its definition. To see all properties, simply right-click and then Go to>Delcation or Usages.

Seeing list of all properties

Conclusion

Adding the ListTile widget helps you boost the app development speed. It follows the material specification and covers everything you need to present an item meaningfully.

In this tutorial, we first looked at how to add the ListTile, its types, and how to manage the theme, and covered some scenarios such as adding a divider and a swipe-to-dismiss feature and changing ListTile height, all of which I hope you will find helpful in building your next list.

: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

.
Pinkesh Darji I love to solve problems using technology that improves users' lives on a major scale. Over the last seven-plus years, I've been developing and leading various mobile apps in different areas.

Leave a Reply