Flutter Material Widgets: The Complete Catalogue With Examples

Coding Liquids blog cover featuring Sagnik Bhattacharya for the complete Flutter Material widgets catalogue, with Scaffold, AppBar, buttons, dialogs, and ListTile component visuals.
Coding Liquids blog cover featuring Sagnik Bhattacharya for the complete Flutter Material widgets catalogue.

Material widgets are the ready-made components Flutter gives you for building Google-style apps — the app bars, buttons, text fields, switches, cards, dialogs, and snack bars you assemble into real screens. This catalogue walks through the Material widgets you will actually use, grouped by job, with a runnable snippet for each. It targets Material 3, which is the default in current Flutter.

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 widget here is a Material widget, which means it expects a MaterialApp somewhere above it and reads colours and shapes from your ThemeData. If a widget throws "No Material widget found", it is because you used it outside a MaterialApp or Scaffold. Pair this catalogue with the layout widgets guide — layout widgets arrange these Material components on the screen.

Follow me on Instagram@sagnikteaches

Use this as a reference. Read it once to learn what exists, then jump back whenever you need the exact constructor for a button, a dialog, or a list tile. Each snippet is small enough to drop into a Scaffold body or an onPressed callback in a fresh project.

Connect on LinkedInSagnik Bhattacharya

The widgets are grouped the way you reach for them: app scaffolding first, then buttons, then inputs and selection, then surfaces and information display, then feedback and overlays, then pickers. A worked settings screen at the end shows how they fit together.

Subscribe on YouTube@codingliquids

The foundation: MaterialApp and ThemeData

Everything starts with MaterialApp. It sets up theming, routing, and localisation. With Material 3 you generate a whole colour scheme from one seed colour.

MaterialApp(
  title: 'My App',
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
    useMaterial3: true, // default in current Flutter
  ),
  home: const HomeScreen(),
)

Inside the app, read theme values with Theme.of(context) rather than hard-coding colours, so your widgets adapt to light and dark mode automatically.

App scaffolding widgets

Scaffold

Scaffold is the page skeleton. It provides slots for an app bar, body, floating action button, drawer, bottom navigation, and snack bars. Almost every screen is a Scaffold.

Scaffold(
  appBar: AppBar(title: const Text('Home')),
  body: const Center(child: Text('Body content')),
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: const Icon(Icons.add),
  ),
)

AppBar

AppBar is the top bar. It holds a leading widget, a title, and trailing actions. Use IconButtons in actions for toolbar buttons.

AppBar(
  title: const Text('Inbox'),
  actions: [
    IconButton(icon: const Icon(Icons.search), onPressed: () {}),
    IconButton(icon: const Icon(Icons.more_vert), onPressed: () {}),
  ],
)

Drawer

A Drawer is the slide-in side menu. Put it in Scaffold.drawer and Flutter adds the hamburger button automatically.

Drawer(
  child: ListView(
    children: const [
      DrawerHeader(child: Text('Menu')),
      ListTile(leading: Icon(Icons.home), title: Text('Home')),
      ListTile(leading: Icon(Icons.settings), title: Text('Settings')),
    ],
  ),
)

NavigationBar and BottomNavigationBar

For bottom tabs, Material 3 uses NavigationBar (the older widget is BottomNavigationBar). Track the selected index in state and rebuild the body.

NavigationBar(
  selectedIndex: _index,
  onDestinationSelected: (i) => setState(() => _index = i),
  destinations: const [
    NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
    NavigationDestination(icon: Icon(Icons.search), label: 'Search'),
    NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
  ],
)

TabBar, NavigationRail, and FloatingActionButton

TabBar with TabBarView (wrapped in a DefaultTabController) gives you swipeable top tabs. NavigationRail is the side navigation used on tablets and desktop. FloatingActionButton is the round primary action button — use FloatingActionButton.extended when you want a label beside the icon.

FloatingActionButton.extended(
  onPressed: () {},
  icon: const Icon(Icons.edit),
  label: const Text('Compose'),
)
The Complete Flutter Guide course thumbnail

Learn these widgets in a structured path

The Complete Flutter Guide turns this catalogue into a full app, with navigation, state, and real backends.

Enrol now

Buttons

Material 3 has four text-and-fill button styles, ordered by emphasis. Pick by how important the action is, not by how it looks.

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    FilledButton(onPressed: () {}, child: const Text('Primary action')),
    FilledButton.tonal(onPressed: () {}, child: const Text('Tonal (secondary)')),
    ElevatedButton(onPressed: () {}, child: const Text('Elevated (with shadow)')),
    OutlinedButton(onPressed: () {}, child: const Text('Outlined (medium)')),
    TextButton(onPressed: () {}, child: const Text('Text (low emphasis)')),
  ],
)

Notes that save time: pass onPressed: null to disable any button. Use the .icon constructors (for example FilledButton.icon) to add an icon beside the label. For an icon-only button use IconButton, and for a group of mutually exclusive options use SegmentedButton:

SegmentedButton<int>(
  segments: const [
    ButtonSegment(value: 0, label: Text('Day')),
    ButtonSegment(value: 1, label: Text('Week')),
    ButtonSegment(value: 2, label: Text('Month')),
  ],
  selected: {_range},
  onSelectionChanged: (s) => setState(() => _range = s.first),
)

Inputs and selection

TextField and TextFormField

TextField is the basic text input; TextFormField adds validation when used inside a Form. Style it with an InputDecoration.

TextField(
  decoration: const InputDecoration(
    labelText: 'Email',
    hintText: 'you@example.com',
    border: OutlineInputBorder(),
    prefixIcon: Icon(Icons.email),
  ),
  keyboardType: TextInputType.emailAddress,
  onChanged: (value) {},
)

For full validation patterns — controllers, focus, error messages, and submitting a form — see the form validation best practices guide.

Checkbox, Radio, and Switch

These are the boolean and single-choice selectors. Each is controlled: you pass the current value and update state in the callback.

Switch(value: _on, onChanged: (v) => setState(() => _on = v)),
Checkbox(value: _checked, onChanged: (v) => setState(() => _checked = v ?? false)),
Radio<int>(value: 1, groupValue: _choice, onChanged: (v) => setState(() => _choice = v!)),

For a tappable row with a label, prefer SwitchListTile, CheckboxListTile, or RadioListTile — they give a full-width touch target and a built-in title.

Slider, DropdownMenu, and Chips

Slider picks a number from a range. DropdownMenu (Material 3) is the modern select control. Chip and its variants (FilterChip, ChoiceChip, ActionChip, InputChip) show compact, tappable attributes.

Slider(
  value: _volume,
  min: 0,
  max: 100,
  divisions: 10,
  label: '${_volume.round()}',
  onChanged: (v) => setState(() => _volume = v),
)

DropdownMenu<String>(
  initialSelection: 'Medium',
  onSelected: (value) {},
  dropdownMenuEntries: const [
    DropdownMenuEntry(value: 'Low', label: 'Low'),
    DropdownMenuEntry(value: 'Medium', label: 'Medium'),
    DropdownMenuEntry(value: 'High', label: 'High'),
  ],
)

Surfaces and information display

Card and ListTile

Card is a rounded, elevated surface for grouping content. ListTile is the standard list row — leading icon, title, subtitle, and a trailing widget. They pair constantly.

Card(
  child: ListTile(
    leading: const CircleAvatar(child: Icon(Icons.person)),
    title: const Text('Sagnik Bhattacharya'),
    subtitle: const Text('Flutter developer'),
    trailing: const Icon(Icons.chevron_right),
    onTap: () {},
  ),
)

ExpansionTile, Badge, Divider, and DataTable

ExpansionTile is a list row that expands to reveal children — perfect for FAQs and settings groups. Badge shows a small count over an icon. Divider draws a thin rule between items. DataTable renders genuinely tabular data with sortable columns.

Badge(
  label: const Text('3'),
  child: const Icon(Icons.notifications),
)

ExpansionTile(
  title: const Text('Advanced settings'),
  children: const [
    ListTile(title: Text('Option A')),
    ListTile(title: Text('Option B')),
  ],
)
The Complete Flutter Guide course thumbnail

Build a real app with these components

Knowing the widgets is the start. The Complete Flutter Guide shows you how to compose them into shippable Android, iOS and web apps.

Enrol now

Feedback and overlays

SnackBar

Show a brief message with ScaffoldMessenger. Add an action for an undo affordance.

ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(
    content: const Text('Item deleted'),
    action: SnackBarAction(label: 'Undo', onPressed: () {}),
  ),
)

AlertDialog and showDialog

showDialog returns a Future that completes when the dialog closes. Return a value with Navigator.pop(context, value) to capture the user's choice.

final confirmed = await showDialog<bool>(
  context: context,
  builder: (context) => AlertDialog(
    title: const Text('Delete item?'),
    content: const Text('This cannot be undone.'),
    actions: [
      TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancel')),
      FilledButton(onPressed: () => Navigator.pop(context, true), child: const Text('Delete')),
    ],
  ),
);
if (confirmed == true) {
  // perform the delete
}

showModalBottomSheet, MaterialBanner, Tooltip, and progress

showModalBottomSheet slides a panel up for actions or extra content. MaterialBanner is a persistent message at the top. Tooltip shows a label on long-press or hover. For loading states use CircularProgressIndicator or LinearProgressIndicator.

showModalBottomSheet(
  context: context,
  builder: (context) => Wrap(
    children: const [
      ListTile(leading: Icon(Icons.share), title: Text('Share')),
      ListTile(leading: Icon(Icons.edit), title: Text('Edit')),
      ListTile(leading: Icon(Icons.delete), title: Text('Delete')),
    ],
  ),
)

Date and time pickers

Flutter ships ready-made pickers. Both return a Future with the user's selection (or null if cancelled).

final date = await showDatePicker(
  context: context,
  initialDate: DateTime.now(),
  firstDate: DateTime(2020),
  lastDate: DateTime(2030),
);

final time = await showTimePicker(
  context: context,
  initialTime: TimeOfDay.now(),
);

Ink and touch feedback: Material and InkWell

The Material ripple effect needs a Material ancestor. InkWell adds a ripple and tap handling to any widget — wrap a custom card in InkWell to make it feel native.

Material(
  color: Colors.transparent,
  child: InkWell(
    borderRadius: BorderRadius.circular(12),
    onTap: () {},
    child: const Padding(
      padding: EdgeInsets.all(16),
      child: Text('Tap me — I ripple'),
    ),
  ),
)

A worked example: a settings screen

This screen combines Scaffold, AppBar, ListView, SwitchListTile, ListTile, Card, a dialog, and a SnackBar — a realistic slice of an app.

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: SettingsScreen()));

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});
  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool _notifications = true;
  bool _darkMode = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Settings')),
      body: ListView(
        padding: const EdgeInsets.all(12),
        children: [
          Card(
            child: Column(
              children: [
                SwitchListTile(
                  title: const Text('Notifications'),
                  subtitle: const Text('Push alerts for new messages'),
                  value: _notifications,
                  onChanged: (v) => setState(() => _notifications = v),
                ),
                const Divider(height: 1),
                SwitchListTile(
                  title: const Text('Dark mode'),
                  value: _darkMode,
                  onChanged: (v) => setState(() => _darkMode = v),
                ),
              ],
            ),
          ),
          const SizedBox(height: 12),
          Card(
            child: ListTile(
              leading: const Icon(Icons.delete_outline, color: Colors.red),
              title: const Text('Delete account'),
              onTap: () => _confirmDelete(context),
            ),
          ),
        ],
      ),
    );
  }

  Future<void> _confirmDelete(BuildContext context) async {
    final confirmed = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Delete account?'),
        content: const Text('This permanently removes your data.'),
        actions: [
          TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancel')),
          FilledButton(onPressed: () => Navigator.pop(context, true), child: const Text('Delete')),
        ],
      ),
    );
    if (confirmed == true && context.mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Account deleted')),
      );
    }
  }
}

Common Material widget mistakes

  • "No Material widget found." You used a Material widget (like InkWell or ListTile) outside a MaterialApp or Scaffold. Wrap it in a Material or move it under a Scaffold.
  • Calling SnackBar without ScaffoldMessenger. Use ScaffoldMessenger.of(context).showSnackBar(...), not the old Scaffold.of approach.
  • Using context across an await without checking context.mounted. After awaiting a dialog, guard UI calls with if (context.mounted) to avoid using a disposed context.
  • Hard-coding colours. Read from Theme.of(context).colorScheme so the app works in light and dark mode.
  • Forgetting buttons need non-null onPressed to be enabled. A null callback renders the button disabled.
  • Reaching for BottomNavigationBar in a Material 3 app. Prefer NavigationBar for the current design language.

Which Material widget should I use?

  • Page skeleton → Scaffold with AppBar.
  • Primary action → FilledButton; secondary → OutlinedButton; tertiary → TextButton; icon-only → IconButton.
  • Text input → TextField, or TextFormField inside a Form.
  • On/off → Switch (or SwitchListTile for a labelled row).
  • List row → ListTile, grouped in a Card or ListView.
  • Brief message → SnackBar; confirmation → AlertDialog; action sheet → showModalBottomSheet.
  • Pick a date/time → showDatePicker / showTimePicker.
  • Bottom tabs → NavigationBar; top tabs → TabBar + TabBarView.

Frequently asked questions

What are Material widgets in Flutter?

They are Flutter's Material Design components — Scaffold, AppBar, buttons, inputs, Card, ListTile, dialogs, SnackBar, and more. They need a MaterialApp ancestor and follow Material 3 theming.

What is the difference between the four button types?

Emphasis. FilledButton is highest (primary action), ElevatedButton adds a shadow, OutlinedButton is medium (secondary), and TextButton is lowest (tertiary, dialogs, toolbars). All share the same onPressed API.

How do I show a dialog?

Call showDialog with a builder returning an AlertDialog or SimpleDialog. It returns a Future; return a value with Navigator.pop(context, value).

SnackBar or BottomSheet?

Use a SnackBar for a brief, auto-dismissing message (often with undo). Use showModalBottomSheet for a panel of actions or extra content that the user dismisses.

Further reads

Keep going with the tutorials that pair with this catalogue:

Sources: Flutter documentation — Material component widgets, the Material 3 (useMaterial3) guidance, and the Widget catalog (docs.flutter.dev). Verified against current stable Flutter and Material 3.