Headless button component. Handles click interactions, keyboard navigation, and accessibility. Use builder pattern for custom styling.
- 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
You can find this example in our GitHub repository.
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),
),
);
},
);
}
}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,
),
),
);
}
}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,
),
),
],
),
),
);
}
}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,
})The child widget to display. Provide either child or builder.
Called when the button is tapped or activated via keyboard. If null, the button will be considered disabled.
Called when the button is long pressed.
Whether to hide the button and its descendants from accessibility services. Defaults to false.
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.
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.
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.
Whether the button is enabled. Defaults to true.
The cursor to show when hovering over the button. Defaults to SystemMouseCursors.click.
Whether to provide platform-specific feedback on press. Defaults to true.
Optional focus node to control focus behavior.
Whether to automatically focus this button when first built. Defaults to false.
Optional builder that receives the current [NakedButtonState] (and optional child) to drive visuals. Use this instead of callbacks for stateless widgets.
Whether to request focus when the button is pressed. Defaults to false.
Optional tooltip semantics for assistive technologies.
Optional semantic label for screen readers.