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:
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.
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:
ListTile (Flutter Widget of the Week)
Why spend hours working out the perfect item layout with rows, columns, containers, and various amounts of spacing and styling when you could just use a ListTile! ListTile implements the material design pattern for list items for you, leaving you to just worry about what goes in it.
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 the design:
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:
There are also other types of ListTile that exist that allow you to perform a specific action on it.
These are:
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 the code block:
Output:
To swap the secondary (leading) widget and checkbox, you can use the controlAffinity
property and set it to ListTileControlAffinity.leading
.
You can also change the shape of the checkbox by adding the checkboxShape
parameter and setting it to RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))
.
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 the code block:
Output:
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 the code block:
Output:
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:
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 the code block:
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 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 approaches produce the same result, as in the following:
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:
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:
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:
ListTile
widget inside the Dismissible widgetonDismissed
parameter and register a callback. Here you can write the logic to remove the item from the listListView.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:
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 visualDensity
property.
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:
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”.
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.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
One Reply to "How to add ListTile in Flutter: A tutorial with examples"
Hey, this was a great tutorial. Thank you for making it!