Flutter Card and ListTile: Build Clean List Items Fast

Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter Card and ListTile guide, with elevation, leading and trailing slots, and clean list-item visuals.
Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter Card and ListTile guide.

List screens are most of what apps actually show — settings, contacts, feeds, search results. Flutter gives you two widgets that make a polished list item almost free: ListTile lays out a row with the right slots and spacing to Material spec, and Card wraps content in an elevated, rounded surface. Together they turn a fiddly hand-built Row into a few readable lines. This guide covers both, the selectable tile variants, and the gotchas, with paste-ready code.

The Complete Flutter Guide course thumbnail

The Complete Flutter Guide: Build Android, iOS and Web apps

Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.

Enrol now

Every snippet below is paste-ready against current stable Flutter. List items almost always live inside a scrolling list, so the ListView.builder guide is the natural next step — and a network image makes a great leading thumbnail.

Follow me on Instagram@sagnikteaches

We'll cover the ListTile slots, putting tiles inside a Card, the selectable variants like SwitchListTile, the Material 3 card styles, and the small surprises that catch people out.

Connect on LinkedInSagnik Bhattacharya

If you'd rather watch a settings screen or a feed take shape, the channel builds these list patterns into real, scrolling apps.

Subscribe on YouTube@codingliquids

ListTile and its slots

ListTile is a fixed-height row with four named slots: leading at the start, title and subtitle stacked in the middle, and trailing at the end. Add onTap and the whole row becomes a tappable, rippling target.

ListTile(
  leading: const CircleAvatar(child: Icon(Icons.person)),
  title: const Text('Ada Lovelace'),
  subtitle: const Text('Tap to view profile'),
  trailing: const Icon(Icons.chevron_right),
  onTap: () => _openProfile(),
)

That single widget handles padding, vertical centring, text styling, and tap feedback — all to Material spec. Recreating it with a Row takes far more code and rarely matches the spacing. One requirement: a ListTile needs a Material ancestor for its ink effects, which a Scaffold provides automatically.

Card: an elevated surface

Card wraps its child in a rounded, shadowed surface that lifts content off the background. Its main knobs are elevation (shadow depth), shape (corner radius and border), and color.

Card(
  elevation: 2,
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(16),
  ),
  child: const Padding(
    padding: EdgeInsets.all(16),
    child: Text('A self-contained chunk of content'),
  ),
)

Note the default margin of 4 logical pixels on every side, which spaces stacked cards apart for free — set margin: EdgeInsets.zero if you want them flush. A Card has no onTap of its own; to make it tappable, put an InkWell or a ListTile inside so the ripple clips to the rounded shape.

Card plus ListTile: the everyday combo

The most common list-item pattern in real apps is a ListTile inside a Card — the card gives the elevation and rounding, the tile gives the layout and tap handling.

Card(
  child: ListTile(
    leading: const Icon(Icons.notifications),
    title: const Text('Push notifications'),
    subtitle: const Text('Daily summary at 9am'),
    trailing: const Icon(Icons.chevron_right),
    onTap: () {},
  ),
)

Because the ListTile owns the onTap, its ripple is automatically clipped to the card's rounded corners — no manual clipBehavior needed. Drop this whole thing into a ListView.builder and you have a scrollable, tappable list in a handful of lines.

The Complete Flutter Guide course thumbnail

Ship list screens users trust

The Complete Flutter Guide covers cards, tiles, and list performance from first principles through to shipped apps.

Enrol now

Selectable tiles

For settings rows you rarely want a bare Checkbox floating in a Row. The combined tiles — CheckboxListTile, SwitchListTile, and RadioListTile — fuse a ListTile with the control and make the entire row toggle it.

SwitchListTile(
  title: const Text('Dark mode'),
  subtitle: const Text('Follow the system theme'),
  value: _isDark,
  onChanged: (v) => setState(() => _isDark = v),
)

These take the same value and onChanged as the standalone control but give you the full tile layout, a tappable row, and the right control placement for free. They are the idiomatic way to build a settings list.

Material 3 card variants

Material 3 adds two named constructors alongside the default elevated card. Card.filled uses a tonal background instead of a shadow, and Card.outlined uses a border — useful when you want separation without the visual weight of elevation.

Card.outlined(
  child: ListTile(title: const Text('Bordered, no shadow')),
)

Choose the elevated default for content that should feel raised, filled for a subtle grouped block, and outlined for a flat, bordered look that suits dense or form-like screens.

Common mistakes

  • Using a ListTile without a Material ancestor. Its ink effects need one; wrap in a Scaffold, Card, or Material.
  • Expecting Card to be tappable. It has no onTap; put an InkWell or ListTile inside.
  • Fighting the default Card margin. It's 4px on all sides by design; set margin: EdgeInsets.zero to remove it.
  • Hand-building a settings row with Checkbox. CheckboxListTile/SwitchListTile make the whole row tappable for free.
  • Overflowing a long title. ListTile titles can overflow; cap them with maxLines and ellipsis as in the Text guide.

Frequently asked questions

What is a ListTile in Flutter?

A fixed-height Material row with leading, title, subtitle, and trailing slots plus onTap. It handles spacing and tap feedback so you don't hand-build a Row.

How do I make a Card tappable in Flutter?

Card has no onTap; put an InkWell or a ListTile inside so the ripple clips to the rounded shape.

How do I add a checkbox or switch to a list row in Flutter?

Use CheckboxListTile, SwitchListTile, or RadioListTile — a ListTile fused with the control, with the whole row tappable.

Why does my Flutter Card have extra space around it?

Card has a default 4px margin on all sides. Set margin: EdgeInsets.zero to remove it.

Further reads

Keep going with the tutorials that pair with this guide:

Sources: Flutter documentation — Card, ListTile, CheckboxListTile, SwitchListTile, RadioListTile, and InkWell API references, plus the Material 3 card guidance (docs.flutter.dev). Verified against current stable Flutter.