fossui
Components

List Tile

A list row with an optional leading widget, a title over an optional subtitle, and an optional trailing widget.

Try it live

A settings screen or an account list is mostly one row repeated. FossListTile is that row: an optional leading widget, a title over an optional subtitle, and an optional trailing widget. Only the title is required, and every gap collapses with the slot it belongs to.

List tile
import 'package:fossui/fossui.dart';

FossListTile(
  title: const Text('Notifications'),
  subtitle: const Text('Email, push, and in-app'),
  leading: const Icon(LucideIcons.bell),
  trailing: const Icon(LucideIcons.chevronRight),
  onTap: openNotificationSettings,
);

Slots

The title is clamped to one line and the subtitle to two. leading is themed as an icon and rendered at its own size; trailing takes anything, and an interactive widget there keeps its own gestures and its own focus stop, so a tap on a trailing switch never reaches onTap.

List tile slots
FossListTile(
  title: const Text('Wi-Fi'),
  subtitle: const Text('Studio 5G'),
  leading: const Icon(LucideIcons.wifi),
  trailing: FossSwitch(value: wifi, onChanged: (v) => setState(() => wifi = v)),
);

Variants

filled is the default: the row paints its own tinted, rounded surface, so a plain list reads as a stack of bands. Use plain inside a FossCard or any container that already paints, where a second rectangle would double-draw.

List tile variants
FossCard(
  content: Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      FossListTile(
        title: const Text('Profile'),
        variant: FossListTileVariant.plain,
        trailing: const Icon(LucideIcons.chevronRight),
        onTap: openProfile,
      ),
      const FossSeparator(),
      FossListTile(
        title: const Text('Billing'),
        variant: FossListTileVariant.plain,
        trailing: const Icon(LucideIcons.chevronRight),
        onTap: openBilling,
      ),
    ],
  ),
);

Tapping

onTap turns the whole row into one target and adds hover, pressed, and keyboard activation. Leaving it null keeps the row inert at full opacity, which is a different state from disabled: a read-only row still reads as available content.

FossListTile(
  title: const Text('Storage'),
  subtitle: const Text('42 GB of 128 GB used'),
  onTap: openStorage,
);

Disabled

enabled: false dims the row to 64% and blocks both the pointer and the keyboard, whether or not onTap is set.

Disabled list tile
FossListTile(
  title: const Text('Sync'),
  subtitle: const Text('Paused while offline'),
  leading: const Icon(LucideIcons.refreshCw),
  enabled: false,
  onTap: startSync,
);

Accessibility name

Pass semanticLabel for a row whose visible text does not read well on its own. It is announced instead of the title and subtitle, not in addition to them.

FossListTile(
  title: const Text('2 of 5'),
  subtitle: const Text('Steps done'),
  semanticLabel: 'Onboarding, two of five steps done',
);

One-off styling

Retheming globally is the first choice, but a single row can override its resolved visuals with a FossListTileStyle. backgroundColor is a WidgetStateProperty resolved against the row's state set.

FossListTile(
  title: const Text('Compact'),
  style: const FossListTileStyle(
    minHeight: 44,
    gap: 12,
    padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
  ),
);

API

FossListTile

PropTypeDefaultDescription
titleWidgetrequiredThe primary line, clamped to one line.
subtitleWidget?nullSecondary line below the title, clamped to two lines.
variantFossListTileVariantfilledResting surface: filled or plain.
leadingWidget?nullWidget at the start of the row, themed as an icon.
trailingWidget?nullWidget at the end of the row; an interactive one keeps its own gestures.
onTapVoidCallback?nullCalled on tap, Enter, or Space; null leaves the row inert.
enabledbooltrueWhether the row is interactive. False dims it to 64%.
semanticLabelString?nullAccessibility name announced instead of the title and subtitle.
styleFossListTileStyle?nullPer-instance overrides on the theme.

FossListTileStyle

FieldTypeDefaultDescription
backgroundColorWidgetStateProperty<Color>?nullFill per interactive state.
paddingEdgeInsetsGeometry?nullInner padding around the slots.
gapdouble?nullGap between the leading slot, the text column, and the trailing slot.
minHeightdouble?nullMinimum row height; a two-line subtitle or a taller slot grows it.
borderRadiusdouble?nullUniform corner radius of the fill, in logical pixels.
titleStyleTextStyle?nullMerged over the resolved title style.
subtitleStyleTextStyle?nullMerged over the resolved subtitle style.
iconSizedouble?nullIcon size for the leading and trailing slots.

Live demo

Open the interactive gallery to try every slot combination and both variants with live knobs.

On this page