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.dart while providing visual hints on hover or long-press.
  • Semantics: Explicitly wraps the IconButton when you need custom announcements distinct from visual text, using SemanticsProperties from packages/flutter/lib/src/widgets/semantics.dart.
  • Theme consistency: Access ThemeData and IconThemeData from packages/flutter/lib/src/material/theme.dart to ensure your text styles and icon colors match the surrounding interface.

Summary

  • IconButton in packages/flutter/lib/src/material/icon_button.dart renders only icons and contains no built-in text parameters.
  • Compose a Column with MainAxisSize.min to stack a Text widget below an IconButton for vertical labels.
  • Implement Tooltip to provide accessible descriptions that appear on hover and long-press while populating the semantics tree.
  • Create a reusable LabeledIconButton widget to maintain consistent spacing, typography, and behavior across your application.
  • Use TextButton.icon when horizontal icon-text alignment is acceptable, avoiding custom composition overhead.
  • Wrap interactive elements with Semantics to 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →