---
title: NakedButton
description: Headless button component for Flutter with full control over styling, behavior, and accessibility features
keywords: [flutter, button, headless, unstyled, accessibility, interaction, hover, focus, press]
---

Headless button component. Handles click interactions, keyboard navigation, and accessibility. Use builder pattern for custom styling.

## When to use this

- **Primary actions**: Submit forms, confirm dialogs, save data
- **Navigation**: Navigate between screens or sections
- **Trigger functions**: Show modals, start processes, toggle features
- **Custom designs**: When standard button styles don't match your design system

<Info>
  You can find this example in our [GitHub repository](https://github.com/btwld/naked_ui/blob/main/packages/example/lib/api/naked_button.0.dart).
</Info>
## Basic implementation

<CodeGroup title="Basic implementation" defaultLanguage="dart">
```dart
import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';

class ButtonExample extends StatelessWidget {
  const ButtonExample({super.key});

  @override
  Widget build(BuildContext context) {
    return NakedButton(
      onPressed: () {},
      builder: (context, state, child) {
        return Container(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
          decoration: BoxDecoration(
            color: state.isPressed ? Colors.blue.shade700 : Colors.blue,
            borderRadius: BorderRadius.circular(8),
          ),
          child: const Text(
            'Button',
            style: TextStyle(color: Colors.white),
          ),
        );
      },
    );
  }
}
```
</CodeGroup>

<CodeGroup title="Icon Button with Tooltip" defaultLanguage="dart">
```dart
import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';

class IconButtonExample extends StatefulWidget {
  const IconButtonExample({super.key});

  @override
  State<IconButtonExample> createState() => _IconButtonExampleState();
}

class _IconButtonExampleState extends State<IconButtonExample> {
  bool _isFavorited = false;

  @override
  Widget build(BuildContext context) {
    return NakedButton(
      onPressed: () {
        setState(() => _isFavorited = !_isFavorited);
      },
      tooltip: _isFavorited ? 'Remove from favorites' : 'Add to favorites',
      child: Container(
        width: 48,
        height: 48,
        decoration: BoxDecoration(
          shape: BoxShape.circle,
          color: Colors.grey.shade100,
        ),
        child: Icon(
          _isFavorited ? Icons.favorite : Icons.favorite_border,
          color: _isFavorited ? Colors.red : Colors.grey.shade600,
        ),
      ),
    );
  }
}
```
</CodeGroup>

<CodeGroup title="Loading Button" defaultLanguage="dart">
```dart
import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';

class LoadingButtonExample extends StatefulWidget {
  const LoadingButtonExample({super.key});

  @override
  State<LoadingButtonExample> createState() => _LoadingButtonExampleState();
}

class _LoadingButtonExampleState extends State<LoadingButtonExample> {
  bool _isLoading = false;

  Future<void> _handleSubmit() async {
    setState(() => _isLoading = true);

    // Simulate API call
    await Future.delayed(const Duration(seconds: 2));

    setState(() => _isLoading = false);
  }

  @override
  Widget build(BuildContext context) {
    return NakedButton(
      onPressed: _isLoading ? null : _handleSubmit,
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        decoration: BoxDecoration(
          color: _isLoading ? Colors.grey.shade300 : Colors.blue,
          borderRadius: BorderRadius.circular(8),
        ),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            if (_isLoading) ...[
              const SizedBox(
                width: 16,
                height: 16,
                child: CircularProgressIndicator(strokeWidth: 2),
              ),
              const SizedBox(width: 8),
            ],
            Text(
              _isLoading ? 'Loading...' : 'Submit',
              style: const TextStyle(
                color: Colors.white,
                fontWeight: FontWeight.w500,
              ),
            ),
          ],
        ),
      ),
    );
  }
}
```
</CodeGroup>

## Constructor

```dart
const NakedButton({
  Key? key,
  this.child,
  this.onPressed,
  this.onLongPress,
  this.enabled = true,
  this.mouseCursor = SystemMouseCursors.click,
  this.enableFeedback = true,
  this.focusNode,
  this.autofocus = false,
  this.onFocusChange,
  this.onHoverChange,
  this.onPressChange,
  this.builder,
  this.focusOnPress = false,
  this.tooltip,
  this.semanticLabel,
  this.excludeSemantics = false,
})
```

## Properties

#### child  → `Widget?`
The child widget to display. Provide either `child` or `builder`.

#### onPressed → `VoidCallback?`
Called when the button is tapped or activated via keyboard. If null, the button will be considered disabled.

#### onLongPress → `VoidCallback?`
Called when the button is long pressed.

#### excludeSemantics → `bool`
Whether to hide the button and its descendants from accessibility services. Defaults to false.

#### onHoverChange → `ValueChanged<bool>?`
Called when hover state changes. Provides the current hover state (true when hovered, false otherwise). **Note**: This is legacy - prefer using the `builder` pattern to access `state.isHovered`.

#### onPressChange → `ValueChanged<bool>?`
Called when pressed state changes. Provides the current pressed state (true when pressed, false otherwise). **Note**: This is legacy - prefer using the `builder` pattern to access `state.isPressed`.

#### onFocusChange → `ValueChanged<bool>?`
Called when focus state changes. Provides the current focus state (true when focused, false otherwise). **Note**: This is legacy - prefer using the `builder` pattern to access `state.isFocused`.

#### enabled → `bool`
Whether the button is enabled. Defaults to true.

#### mouseCursor → `MouseCursor`
The cursor to show when hovering over the button. Defaults to SystemMouseCursors.click.

#### enableFeedback → `bool`
Whether to provide platform-specific feedback on press. Defaults to true.

#### focusNode → `FocusNode?`
Optional focus node to control focus behavior.

#### autofocus → `bool`
Whether to automatically focus this button when first built. Defaults to false.

#### builder → `ValueWidgetBuilder<NakedButtonState>?`
Optional builder that receives the current [NakedButtonState] (and optional `child`) to drive visuals. Use this instead of callbacks for stateless widgets.

#### focusOnPress → `bool`
Whether to request focus when the button is pressed. Defaults to false.

#### tooltip → `String?`
Optional tooltip semantics for assistive technologies.

#### semanticLabel → `String?`
Optional semantic label for screen readers.
