IconButton is a Flutter Material widget that renders a single icon as a tappable control, typically used for actions like favorite, share, or close where a text label would add unnecessary clutter.
Material 3 provides .filled, .filledTonal, and .outlined variants, as well as support for selected states through isSelected. Unlike TextButton.icon or FilledButton.icon, which combine an icon with a visible text label, IconButton is designed for icon-only actions.
IconButton is part of Flutter's Material library and requires no external package.IconButton.filled, IconButton.filledTonal, and IconButton.outlined.isSelected makes the button represent a selected state; when selectedIcon is provided, Flutter displays it while the button is selected. The app is responsible for changing the isSelected value.CupertinoIconButton class – the iOS-style pattern is CupertinoButton with an Icon (or another widget) as its child.onPressed to null disables the button. For an icon-only button, providing a tooltip is recommended to give it an accessible label.A plain IconButton needs only an icon and an onPressed callback, but a tooltip is what makes it accessible.
import 'package:flutter/material.dart';
class FavoriteButton extends StatelessWidget {
const FavoriteButton({super.key, required this.onToggle});
final VoidCallback onToggle;
@override
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.favorite_border),
tooltip: 'Add to favorites',
onPressed: onToggle,
);
}
}Without a tooltip, an icon-only button may not have a meaningful accessible label for screen reader users. Providing a descriptive tooltip helps communicate the button's purpose.
With isSelected, an IconButton can reflect whether an action is currently active. If you provide selectedIcon, Flutter uses it whenever the button is in the selected state.
import 'package:flutter/material.dart';
class LikeToggle extends StatefulWidget {
const LikeToggle({super.key});
@override
State<LikeToggle> createState() => _LikeToggleState();
}
class _LikeToggleState extends State<LikeToggle> {
bool _liked = false;
@override
Widget build(BuildContext context) {
return IconButton.filledTonal(
isSelected: _liked,
icon: const Icon(Icons.favorite_border),
selectedIcon: const Icon(Icons.favorite),
tooltip: _liked ? 'Unlike' : 'Like',
onPressed: () => setState(() => liked = !liked),
);
}
}IconButton.filledTonal gives the toggle a visible background so its selected state reads clearly, which a plain IconButton can't show on its own.
AppBar, list tile, or toolbar.isSelected rather than maintaining separate widgets for each state.TextButton.icon, OutlinedButton.icon, or FilledButton.icon instead.CupertinoButton with an icon as its child.FilledButton or another labeled button may be a better choice.| IconButton (Material) | CupertinoButton (icon child) |
|---|---|
Styles | |
| .filled, .filledTonal, .outlined | .filled, .tinted |
Toggle support | |
| Built in via isSelected and selectedIcon | Not built in – manage state manually |
Layout / sizing | |
| Designed as a compact icon control | General-purpose button whose padding and sizing can be customized |
Accessibility | |
| Use tooltip to provide a descriptive label for an icon-only button | Use an appropriate semantic label or surrounding text as needed |
Best for | |
| Material or cross-platform apps | iOS-focused interfaces that use Cupertino styling and behavior |
Verdict: use IconButton in Material or adaptive apps; use CupertinoButton with an icon child when building an interface specifically for iOS.
tooltip for icon-only controls so their purpose is clear to screen reader users.isSelected and selectedIcon to represent a selected state instead of maintaining separate IconButton widgets for each state..filled or .filledTonal when the icon needs stronger visual emphasis than a plain IconButton.IconButton.iconSize rather than Icon.size, so the button can size its splash area appropriately.tooltip. An icon-only button may lack a meaningful accessible label, and sighted users also lose the hover hint on desktop and web.IconButton widgets for each state. Use isSelected and selectedIcon to represent the selected state instead; the app still controls the isSelected value.Icon.size directly or overriding the button's constraints just to resize an IconButton. Use iconSize on IconButton instead: it lets the button size its splash area appropriately for the icon. Setting a larger size through Icon.size instead may leave the button sized for its default 24-pixel icon, causing the larger icon to be clipped.IconButton when the action's meaning is not clear from the icon alone. If users need a text label to understand the action, use TextButton.icon, OutlinedButton.icon, or FilledButton.icon instead.IconButton by setting its color to grey instead of passing null to onPressed. The button stays tappable and announces as enabled; only onPressed: null truly disables it.
Find the most important things to keep in mind when introducing RTL design in your Flutter mobile app and how you can use Flutter to support various text directions with little effort. Follow the guide on RTL prepared by Flutter Developer.

Flutter is loved by many for its simple and fast development of high-quality cross-platform apps. Let’s take a closer look if Flutter is a suitable solution in every case, i.e., when developing mobile, web, and desktop applications.