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: Build Android, iOS and Web apps
Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.
Enrol nowEvery 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.
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.
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.
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'),
)

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 nowButtons
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')),
],
)

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 nowFeedback 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
InkWellorListTile) outside aMaterialApporScaffold. Wrap it in aMaterialor move it under aScaffold. - Calling SnackBar without ScaffoldMessenger. Use
ScaffoldMessenger.of(context).showSnackBar(...), not the oldScaffold.ofapproach. - Using context across an await without checking
context.mounted. After awaiting a dialog, guard UI calls withif (context.mounted)to avoid using a disposed context. - Hard-coding colours. Read from
Theme.of(context).colorSchemeso the app works in light and dark mode. - Forgetting buttons need non-null
onPressedto be enabled. Anullcallback renders the button disabled. - Reaching for
BottomNavigationBarin a Material 3 app. PreferNavigationBarfor the current design language.
Which Material widget should I use?
- Page skeleton →
ScaffoldwithAppBar. - Primary action →
FilledButton; secondary →OutlinedButton; tertiary →TextButton; icon-only →IconButton. - Text input →
TextField, orTextFormFieldinside aForm. - On/off →
Switch(orSwitchListTilefor a labelled row). - List row →
ListTile, grouped in aCardorListView. - 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:
- Flutter Development Guide 2026 — the full Flutter hub.
- Flutter Layout Widgets: The Complete Guide — how to arrange these Material components on screen.
- Flutter Cupertino Widgets: Build iOS-Style UIs — the iOS-style counterparts to these Material widgets.
- Flutter Scrolling and Slivers: The Complete Guide — put these components into lists, grids, and collapsing headers.
- Flutter Form Validation Best Practices — production-ready forms with these input widgets.
- How to Fix RenderFlex Overflowed in Flutter — the most common layout error when laying out these widgets.
- Flutter Widget Previewer — iterate on these components without relaunching the app.
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.