Migration to Flutter Guide
Discover our battle-tested 21-step framework for a smooth and successful migration to Flutter!
Home
Glossary

Buttons in Flutter

What are buttons in Flutter?

Buttons in Flutter are tappable Material widgets that trigger an action through an onPressed callback, ranging from high-emphasis filled buttons to icon-only and floating variants.

Material 3 organizes them into five emphasis levels – filled, filled tonal, elevated, outlined, and text – plus IconButton and FloatingActionButton for icon-driven actions. Buttons differ from GestureDetector or InkWell, which detect taps on arbitrary widgets but add no built-in Material styling, states, or accessibility semantics.

Key facts at a glance

  • Buttons in Flutter live in the Material library and need a Material ancestor (Scaffold, Card, or Material) to render correctly.
  • Material 3 defines five main button styles in Flutter: FilledButton, FilledButton.tonal, ElevatedButton, OutlinedButton, and TextButton.
  • IconButton and FloatingActionButton cover icon-only and primary floating actions.
  • Every button widget disables itself automatically when its onPressed callback is null.
  • Flutter uses a 48x48 logical-pixel minimum interactive dimension (kMinInteractiveDimension) by default for many Material controls, helping provide an accessible tap target.
  • FilledButton and FilledButton.tonal require Flutter 3.7 or later. The Material 3 IconButton.filled, IconButton.filledTonal, and IconButton.outlined variants require Flutter 3.10 or later.
  • RaisedButton, FlatButton, or OutlineButton from an old tutorial were deprecated starting in Flutter 2.0 and completely removed in Flutter 3.0.

Which button type should I use in Flutter?

  • FilledButton – the current Material 3 default for a single primary action per screen (Save, Submit, Continue). No shadow, just a fully colored background using the theme's primary color.
  • FilledButton.tonal – a secondary-color filled variant for a second important action next to a FilledButton, without competing for attention.
  • ElevatedButton – the older Material 2 raised button with elevation. It still ships with Flutter, but Material 3 favors FilledButton for important, final actions.
  • OutlinedButton – a bordered, unfilled button for medium-emphasis actions, like "Cancel" next to a filled primary action.
  • TextButton – the lowest-emphasis button: no border, no fill, just colored text. Good for dialog actions or dense lists of secondary actions.
  • IconButton – an icon-only tappable control, with .filled, .filledTonal, and .outlined Material 3 variants; it can also double as a toggle via isSelected.
  • FloatingActionButton (FAB) – a circular button that floats above the screen content, reserved for a primary screen-level action; also available as .small, .large, and .extended.

How do I add a button in Flutter?

FilledButton is the fastest way to add a Material 3 primary action button.

import 'package:flutter/material.dart';

class SaveButton extends StatelessWidget {
  const SaveButton({super.key, required this.onSave});

  final VoidCallback? onSave;

  @override
  Widget build(BuildContext context) {
    return FilledButton(
      onPressed: onSave,
      child: const Text('Save'),
    );
  }
}

Passing null instead of a callback to onPressed disables the button and mutes its color automatically – no manual enabled flag needed.

How do I add an icon to a Flutter button?

Every Material 3 button class provides the .icon named constructor for pairing an icon with a label; FilledButton also provides the tonalIcon constructor for the tonal variant. The style parameter accepts a ButtonStyle that you can conveniently create with styleFrom.

import 'package:flutter/material.dart';

class AddItemButton extends StatelessWidget {
  const AddItemButton({super.key, required this.onAdd});

  final VoidCallback onAdd;

  @override
  Widget build(BuildContext context) {
    return OutlinedButton.icon(
      onPressed: onAdd,
      icon: const Icon(Icons.add),
      label: const Text('Add item'),
      style: OutlinedButton.styleFrom(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(24),
        ),
      ),
    );
  }
}

The same .icon pattern and styleFrom approach work on FilledButton, ElevatedButton, and TextButton, not just OutlinedButton.

When should I use a button widget – and when should I reach for GestureDetector instead?

Use a button widget when

  • The tap target represents a semantic action (submit, save, delete, navigate) that should look and behave like the rest of the app's Material theme.
  • You need built-in disabled states, focus/hover handling, and screen-reader semantics for free.
  • The design matches one of the five M3 emphasis levels, or a FAB.

Reach for GestureDetector or InkWell instead when

  • You're making a custom, non-standard tappable shape (a card, an image, a whole list tile) that a button widget can't visually accommodate.
  • You need gestures a button doesn't expose, like drag, scale, or double-tap.
  • You're building outside Material and don't need Material's built-in interaction styling. InkWell is specifically designed for Material surfaces and requires a Material ancestor for its ink effects.

FilledButton vs. ElevatedButton

FilledButtonElevatedButton
Spec
M3 high-emphasis button for important, final actionsM3 elevated button for actions that need more visual separation
Visual treatment
Solid primary-color fill, flat by defaultElevated surface with a subtle shadow
Best for
Primary actions that need a clear visual emphasisActions that need to stand out from the surrounding surface
Tonal sibling
FilledButton.tonalNone
Icon variant
FilledButton.iconElevatedButton.icon

Verdict: default to FilledButton for important, final actions on new Material 3 screens; use ElevatedButton when elevation provides useful visual separation from the surrounding surface.

Best practices

  1. Match emphasis to importance – use FilledButton sparingly for important, final actions rather than making every action a filled button.
  2. Give every icon-only IconButton a meaningful tooltip – it describes the button's action and is also exposed to assistive technologies. Use Icon.semanticLabel when the icon itself needs a semantic description; for an IconButton, the tooltip should normally describe the action rather than the icon.
  3. Use .icon constructors instead of manually wrapping a Row – the built-in icon spacing follows the button’s Material styling.
  4. Don't shrink tap targets below kMinInteractiveDimension (48x48), even for tightly designed icon buttons.
  5. Reach for ButtonStyle/styleFrom before writing a fully custom widget – most brand theming fits inside the existing button classes.

Common mistakes

  • Using ElevatedButton as the default primary action in a Material 3 app. FilledButton is intended for important, final actions; use ElevatedButton when elevation provides useful visual separation.
  • Wrapping a Container in GestureDetector to fake a button. This silently drops the disabled states, ripple feedback, and semantics that come free with real button widgets.
  • Ignoring accessibility semantics for icon-only controls. Make sure the control has a meaningful accessible description of its action; use Icon.semanticLabel when the icon itself needs a semantic description.
  • Leaving IconButton without a meaningful tooltip. The tooltip describes the action and is also used for accessibility, so it normally provides the accessible description for an icon-only button.
  • "Disabling" a button with an empty onPressed callback instead of null. An empty callback still reports the button as enabled to accessibility tools.

Performance & accessibility notes

  • Material buttons provide built-in accessibility semantics. Standard buttons expose a button role to assistive technologies; additional Semantics wrappers should complement rather than replace the information already provided, while ExcludeSemantics can remove child semantics from the accessibility tree.
  • Material and Cupertino use different minimum interactive dimensions. kMinInteractiveDimension is 48.0 for Material, while Cupertino's kMinInteractiveDimensionCupertino is 44.0, so platform-specific sizing should be taken into account.

Learn more

Design system Flutter

Building a Design System in a Large Flutter App

Flutter is known for its seamless UI/UX features and robust design elements. It allowed us to deliver a unique customer experience in the “CA24 Mobile” banking app. This article shares some of the learnings and pain points behind implementing it in this large project.

Complex Animations in Flutter

Complex Animations in Flutter

Flutter ships with plenty of high-quality widgets, layouts, and themes that developers can use to speed up the whole creation process. A great example of custom widgets made in Flutter is the Placement Wheel developed for one of our clients. See how to do it.

Widgetbook Entries Generator by Leancode

How We Boosted Moving Flutter Widgets to Widgetbook

At LeanCode, while working on complex Flutter projects, we noticed that integrating widgets into Widgetbook was often repetitive and time-consuming. To address this, we created the Widgetbook Entries Generator - a VSCode extension that simplifies the process. See how it works.