How to Create an Icon Button Flutter Widget with Text Descriptions Below the Icon
Flutter's IconButton widget renders only a tappable icon without built-in text support, so you must compose it inside a Column with a Text widget to display a label underneath while using Tooltip or Semantics to maintain accessibility.
The IconButton class in the flutter/flutter repository provides the standard Material Design icon button implementation, but it intentionally paints only the icon graphic. To build a more usable icon button flutter component that includes descriptive text below the icon—improving clarity for users and accessibility for screen readers—you combine layout widgets from packages/flutter/lib/src/widgets/basic.dart with the button implementation in packages/flutter/lib/src/material/icon_button.dart.
Why IconButton Doesn't Include Labels
In packages/flutter/lib/src/material/icon_button.dart, the IconButton class extends StatelessWidget and builds upon InkResponse to handle tap gestures and splash effects. The widget's build method strictly constrains the child to an Icon widget, offering no parameter for text labels. This design keeps the widget lightweight for toolbar scenarios, but requires manual composition when you need vertical text arrangement.
Building a Vertical Icon Button with Column
To position text below the icon, wrap the IconButton in a Column configured with MainAxisSize.min. This prevents the column from expanding to fill available space while maintaining tight vertical packing.
Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.camera_alt),
tooltip: 'Take a picture', // Accessibility hint
onPressed: () {
// Camera functionality
},
),
const SizedBox(height: 4),
const Text(
'Camera',
style: TextStyle(fontSize: 12),
),
],
)
The tooltip parameter serves dual purposes: it displays a hover or long-press hint for sighted users and populates the semantics tree for screen readers in packages/flutter/lib/src/material/tooltip.dart.
Creating a Reusable LabeledIconButton Widget
For consistent styling across your application, encapsulate the pattern in a custom StatelessWidget that integrates with Flutter's theme system.
class LabeledIconButton extends StatelessWidget {
final IconData icon;
final String label;
final VoidCallback onPressed;
final String? tooltip;
const LabeledIconButton({
Key? key,
required this.icon,
required this.label,
required this.onPressed,
this.tooltip,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final semanticsLabel = tooltip ?? label;
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Semantics(
button: true,
label: semanticsLabel,
child: IconButton(
icon: Icon(icon),
tooltip: tooltip,
onPressed: onPressed,
),
),
const SizedBox(height: 4),
Text(
label,
style: Theme.of(context).textTheme.bodySmall,
),
],
);
}
}
Usage example:
LabeledIconButton(
icon: Icons.favorite,
label: 'Like',
tooltip: 'Mark as liked',
onPressed: () => debugPrint('Liked!'),
)
This implementation uses the Semantics widget from packages/flutter/lib/src/widgets/semantics.dart to explicitly mark the element as a button with a descriptive label, ensuring TalkBack and VoiceOver announce the action correctly even if the visual text is suppressed in future design iterations.
Alternative Approach: TextButton.icon for Horizontal Layouts
If your design allows the label to appear to the right of the icon rather than below it, Flutter provides TextButton.icon in the Material library. This built-in widget handles the composition internally.
TextButton.icon(
icon: const Icon(Icons.send),
label: const Text('Send'),
onPressed: () {
// Send action
},
)
While this approach requires less code, it does not support vertical stacking of icon above text without significant customization of the button's internal layout.
Accessibility and Semantics Considerations
When creating labeled icon buttons, always consider how assistive technologies interpret your widget tree:
- Tooltip: Automatically adds semantic labels in
packages/flutter/lib/src/material/tooltip.dartwhile providing visual hints on hover or long-press. - Semantics: Explicitly wraps the
IconButtonwhen you need custom announcements distinct from visual text, usingSemanticsPropertiesfrompackages/flutter/lib/src/widgets/semantics.dart. - Theme consistency: Access
ThemeDataandIconThemeDatafrompackages/flutter/lib/src/material/theme.dartto ensure your text styles and icon colors match the surrounding interface.
Summary
IconButtoninpackages/flutter/lib/src/material/icon_button.dartrenders only icons and contains no built-in text parameters.- Compose a
ColumnwithMainAxisSize.minto stack aTextwidget below anIconButtonfor vertical labels. - Implement
Tooltipto provide accessible descriptions that appear on hover and long-press while populating the semantics tree. - Create a reusable
LabeledIconButtonwidget to maintain consistent spacing, typography, and behavior across your application. - Use
TextButton.iconwhen horizontal icon-text alignment is acceptable, avoiding custom composition overhead. - Wrap interactive elements with
Semanticsto ensure screen readers properly announce button actions and labels.
Frequently Asked Questions
Can IconButton display text by default?
No. The IconButton class specifically constrains its child to an Icon widget and provides no properties for text labels. According to the source in packages/flutter/lib/src/material/icon_button.dart, the widget is designed exclusively for icon-only scenarios; you must compose it with separate Text widgets or use alternative buttons like TextButton.icon for built-in labeling.
How do I adjust the spacing between the icon and text?
Insert a SizedBox with a specific height between the IconButton and Text widgets within your Column. The example uses const SizedBox(height: 4), but you can adjust this value or use Padding widgets to achieve the exact visual separation required by your design system.
Is there a Material widget that combines icon and label vertically?
Flutter's Material library does not provide a dedicated widget for vertical icon-text buttons. You must compose IconButton with Column and Text manually, or use third-party packages. The NavigationBar and BottomNavigationBar widgets do support vertical labels, but they are designed specifically for navigation patterns rather than general-purpose buttons.
How do I ensure screen readers announce the button label correctly?
Wrap the IconButton in a Semantics widget with button: true and provide a descriptive label property. Alternatively, use the tooltip parameter on IconButton, which automatically injects the text into the semantics tree via the Tooltip widget implementation in packages/flutter/lib/src/material/tooltip.dart. This ensures TalkBack on Android and VoiceOver on iOS announce the action clearly.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →