ListTile class

A single fixed-height row that typically contains some text as well as a leading or trailing icon.

A list tile contains one to three lines of text optionally flanked by icons or other widgets, such as check boxes. The icons (or other widgets) for the tile are defined with the leading and trailing parameters. The first line of text is not optional and is specified with title. The value of subtitle, which is optional, will occupy the space allocated for an additional line of text, or two lines if isThreeLine is true. If dense is true then the overall height of this tile and the size of the DefaultTextStyles that wrap the title and subtitle widget are reduced.

It is the responsibility of the caller to ensure that title does not wrap, and to ensure that subtitle doesn't wrap (if isThreeLine is false) or wraps to two lines (if it is true).

The heights of the leading and trailing widgets are constrained according to the Material spec. An exception is made for one-line ListTiles for accessibility. Please see the example below to see how to adhere to both Material spec and accessibility requirements.

The leading and trailing widgets can expand as far as they wish horizontally, so ensure that they are properly constrained.

List tiles are typically used in ListViews, or arranged in Columns in Drawers and Cards.

This widget requires a Material widget ancestor in the tree to paint itself on, which is typically provided by the app's Scaffold. The tileColor, selectedTileColor, focusColor, and hoverColor are not painted by the ListTile itself but by the Material widget ancestor. In this case, one can wrap a Material widget around the ListTile, e.g.:

link
const ColoredBox(
  color: Colors.green,
  child: Material(
    child: ListTile(
      title: Text('ListTile with red background'),
      tileColor: Colors.red,
    ),
  ),
)

Performance considerations when wrapping ListTile with Material

Wrapping a large number of ListTiles individually with Materials is expensive. Consider only wrapping the ListTiles that require it or include a common Material ancestor where possible.

ListTile must be wrapped in a Material widget to animate tileColor, selectedTileColor, focusColor, and hoverColor as these colors are not drawn by the list tile itself but by the material widget ancestor.

This example showcases how ListTile needs to be wrapped in a Material widget to animate colors.
link

To create a local project with this code sample, run:
flutter create --sample=material.ListTile.2 mysample

This example uses a ListView to demonstrate different configurations of ListTiles in Cards.

Different variations of ListTile

link

To create a local project with this code sample, run:
flutter create --sample=material.ListTile.3 mysample

This sample shows the creation of a ListTile using ThemeData.useMaterial3 flag, as described in: https://m3.material-io.cn/components/lists/overview.
link

To create a local project with this code sample, run:
flutter create --sample=material.ListTile.4 mysample

This sample shows ListTile's textColor and iconColor can use MaterialStateColor color to change the color of the text and icon when the ListTile is enabled, selected, or disabled.
link

To create a local project with this code sample, run:
flutter create --sample=material.ListTile.5 mysample

This sample shows ListTile.titleAlignment can be used to configure the leading and trailing widgets alignment relative to the title and subtitle widgets.
link

To create a local project with this code sample, run:
flutter create --sample=material.ListTile.6 mysample

To use a ListTile within a Row, it needs to be wrapped in an Expanded widget. ListTile requires fixed width constraints, whereas a Row does not constrain its children.
link
const Row(
  children: <Widget>[
    Expanded(
      child: ListTile(
        leading: FlutterLogo(),
        title: Text('These ListTiles are expanded '),
      ),
    ),
    Expanded(
      child: ListTile(
        trailing: FlutterLogo(),
        title: Text('to fill the available space.'),
      ),
    ),
  ],
)

Tiles can be much more elaborate. Here is a tile which can be tapped, but which is disabled when the _act variable is not 2. When the tile is tapped, the whole row has an ink splash effect (see InkWell).
link
ListTile(
  leading: const Icon(Icons.flight_land),
  title: const Text("Trix's airplane"),
  subtitle: _act != 2 ? const Text('The airplane is only in Act II.') : null,
  enabled: _act == 2,
  onTap: () { /* react to the tile being tapped */ }
)

To be accessible, tappable leading and trailing widgets have to be at least 48x48 in size. However, to adhere to the Material spec, trailing and leading widgets in one-line ListTiles should visually be at most 32 (dense: true) or 40 (dense: false) in height, which may conflict with the accessibility requirement.

For this reason, a one-line ListTile allows the height of leading and trailing widgets to be constrained by the height of the ListTile. This allows for the creation of tappable leading and trailing widgets that are large enough, but it is up to the developer to ensure that their widgets follow the Material spec.

Here is an example of a one-line, non-dense ListTile with a tappable leading widget that adheres to accessibility requirements and the Material spec. To adjust the use case below for a one-line, dense ListTile, adjust the vertical padding to 8.0.
link
ListTile(
  leading: GestureDetector(
    behavior: HitTestBehavior.translucent,
    onTap: () {},
    child: Container(
      width: 48,
      height: 48,
      padding: const EdgeInsets.symmetric(vertical: 4.0),
      alignment: Alignment.center,
      child: const CircleAvatar(),
    ),
  ),
  title: const Text('title'),
  dense: false,
)

The ListTile layout isn't exactly what I want

If the way ListTile pads and positions its elements isn't quite what you're looking for, it's easy to create custom list items with a combination of other widgets, such as Rows and Columns.

Here is an example of a custom list item that resembles a YouTube-related video list item created with Expanded and Container widgets.
link

To create a local project with this code sample, run:
flutter create --sample=material.ListTile.10 mysample

Here is an example of an article list item with multiline titles and subtitles. It utilizes Rows and Columns, as well as Expanded and AspectRatio widgets to organize its layout.
link

To create a local project with this code sample, run:
flutter create --sample=material.ListTile.11 mysample

See also:

Inheritance

Constructors

ListTile({Key? key, Widget? leading, Widget? title, Widget? subtitle, Widget? trailing, bool isThreeLine = false, bool? dense, VisualDensity? visualDensity, ShapeBorder? shape, ListTileStyle? style, Color? selectedColor, Color? iconColor, Color? textColor, TextStyle? titleTextStyle, TextStyle? subtitleTextStyle, TextStyle? leadingAndTrailingTextStyle, EdgeInsetsGeometry? contentPadding, bool enabled = true, GestureTapCallback? onTap, GestureLongPressCallback? onLongPress, ValueChanged<bool>? onFocusChange, MouseCursor? mouseCursor, bool selected = false, Color? focusColor, Color? hoverColor, Color? splashColor, FocusNode? focusNode, bool autofocus = false, Color? tileColor, Color? selectedTileColor, bool? enableFeedback, double? horizontalTitleGap, double? minVerticalPadding, double? minLeadingWidth, ListTileTitleAlignment? titleAlignment})
Creates a list tile.
const

Properties

autofocus bool
True if this widget will be selected as the initial focus when no other node in its scope is currently focused.
final
contentPadding EdgeInsetsGeometry?
The tile's internal padding.
final
dense bool?
Whether this list tile is part of a vertically dense list.
final
enabled bool
Whether this list tile is interactive.
final
enableFeedback bool?
Whether detected gestures should provide acoustic and/or haptic feedback.
final
focusColor Color?
The color for the tile's Material when it has the input focus.
final
focusNode FocusNode?
An optional focus node to use as the focus node for this widget.
final
hashCode int
The hash code for this object.
no setterinherited
horizontalTitleGap double?
The horizontal gap between the titles and the leading/trailing widgets.
final
hoverColor Color?
The color for the tile's Material when a pointer is hovering over it.
final
iconColor Color?
Defines the default color for leading and trailing icons.
final
isThreeLine bool
Whether this list tile is intended to display three lines of text.
final
key Key?
Controls how one widget replaces another widget in the tree.
finalinherited
leading Widget?
A widget to display before the title.
final
leadingAndTrailingTextStyle TextStyle?
The text style for ListTile's leading and trailing.
final
minLeadingWidth double?
The minimum width allocated for the ListTile.leading widget.
final
minVerticalPadding double?
The minimum padding on the top and bottom of the title and subtitle widgets.
final
mouseCursor MouseCursor?
The cursor for a mouse pointer when it enters or is hovering over the widget.
final
onFocusChange ValueChanged<bool>?
Handler called when the focus changes.
final
onLongPress GestureLongPressCallback?
Called when the user long-presses on this list tile.
final
onTap GestureTapCallback?
Called when the user taps this list tile.
final
runtimeType Type
A representation of the runtime type of the object.
no setterinherited
selected bool
If this tile is also enabled then icons and text are rendered with the same color.
final
selectedColor Color?
Defines the color used for icons and text when the list tile is selected.
final
selectedTileColor Color?
Defines the background color of ListTile when selected is true.
final
shape ShapeBorder?
Defines the tile's InkWell.customBorder and Ink.decoration shape.
final
splashColor Color?
The color of splash for the tile's Material.
final
style ListTileStyle?
Defines the font used for the title.
final
subtitle Widget?
Additional content displayed below the title.
final
subtitleTextStyle TextStyle?
The text style for ListTile's subtitle.
final
textColor Color?
Defines the text color for the title, subtitle, leading, and trailing.
final
tileColor Color?
Defines the background color of ListTile when selected is false.
final
title Widget?
The primary content of the list tile.
final
titleAlignment ListTileTitleAlignment?
Defines how ListTile.leading and ListTile.trailing are vertically aligned relative to the ListTile's titles (ListTile.title and ListTile.subtitle).
final
titleTextStyle TextStyle?
The text style for ListTile's title.
final
trailing Widget?
A widget to display after the title.
final
visualDensity VisualDensity?
Defines how compact the list tile's layout will be.
final

Methods

build(BuildContext context) Widget
Describes the part of the user interface represented by this widget.
override
createElement() StatelessElement
Creates a StatelessElement to manage this widget's location in the tree.
inherited
debugDescribeChildren() List<DiagnosticsNode>
Returns a list of DiagnosticsNode objects describing this node's children.
inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node.
override
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep.
inherited
toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) String
A string representation of this object.
inherited
toStringDeep({String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug}) String
Returns a string representation of this node and its descendants.
inherited
toStringShallow({String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) String
Returns a one-line detailed description of the object.
inherited
toStringShort() String
A short, textual description of this widget.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited

Static Methods

divideTiles({BuildContext? context, required Iterable<Widget> tiles, Color? color}) Iterable<Widget>
Add a one pixel border in between each tile. If color isn't specified the ThemeData.dividerColor of the context's Theme is used.