Flutter Lesson 11 of 83 3 min read
Buttons in Flutter: FilledButton, ElevatedButton, TextButton and More
Learn every Flutter button: FilledButton, ElevatedButton, OutlinedButton, TextButton, IconButton and FloatingActionButton, with styling.
On this page
Flutter’s Material buttons differ in how much attention they demand. Use the strongest style for the one main action on a screen.
The button family #
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FilledButton(onPressed: () {}, child: const Text('Filled')),
FilledButton.tonal(onPressed: () {}, child: const Text('Tonal')),
ElevatedButton(onPressed: () {}, child: const Text('Elevated')),
OutlinedButton(onPressed: () {}, child: const Text('Outlined')),
TextButton(onPressed: () {}, child: const Text('Text')),
IconButton(onPressed: () {}, icon: const Icon(Icons.share)),
const FilledButton(onPressed: null, child: Text('Disabled')),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
),
),
);
}
| Button | Emphasis | Typical use |
|---|---|---|
FilledButton | Highest | The main action: Save, Pay, Sign in |
FilledButton.tonal | High | An important secondary action |
ElevatedButton | Medium | An action that needs to stand out from a busy background |
OutlinedButton | Medium | Secondary: Cancel, Back |
TextButton | Low | Dialog actions, links |
IconButton | Low | Toolbar actions |
FloatingActionButton | Highest | The one primary action of a screen |
onPressed #
Every button needs onPressed. It is a function that runs on tap.
FilledButton(
onPressed: () {
debugPrint('Saved');
},
child: const Text('Save'),
)
FilledButton(onPressed: _save, child: const Text('Save')) // pass a method by name
Pass null to disable the button. It greys out and ignores taps.
FilledButton(
onPressed: _isValid ? _submit : null,
child: const Text('Submit'),
)
Icon and label #
FilledButton.icon(
onPressed: () {},
icon: const Icon(Icons.download),
label: const Text('Download'),
)
ElevatedButton.icon, OutlinedButton.icon and TextButton.icon work the same way.
Styling one button #
Each button class has a styleFrom helper.
FilledButton(
onPressed: () {},
style: FilledButton.styleFrom(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
child: const Text('Continue'),
)
A full-width button #
SizedBox(
width: double.infinity,
child: FilledButton(onPressed: () {}, child: const Text('Sign in')),
)
Or set minimumSize: const Size.fromHeight(48) in styleFrom.
Styling every button at once #
Set it in the theme, not on each button.
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
minimumSize: const Size.fromHeight(48),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
),
),
home: const HomePage(),
)
A button that shows progress #
FilledButton(
onPressed: _saving ? null : _save,
child: _saving
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Save'),
)
Disabling the button while saving also stops double taps.
Good habits #
- One filled button per screen. If everything shouts, nothing is heard.
- Label with a verb that says what happens: “Send message”, not “OK”.
- Give
IconButtonatooltip. Screen readers use it. - Keep tap targets at least 48 by 48 logical pixels.
Try it yourself #
Build a row with a “Cancel” OutlinedButton and a “Save” FilledButton. Add a checkbox above them, and keep Save disabled until the box is ticked.