NakedButton
Headless button component for Flutter with full control over styling, behavior, and accessibility features
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
You can find this example in our GitHub repository.
Basic implementation
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,
),
),
],
),
),
);
}
}
Constructor
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,
})
onPressed → VoidCallback?
Called when the button is tapped or activated via keyboard. If null, the button will be considered disabled.
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.
mouseCursor → MouseCursor
The cursor to show when hovering over the button. Defaults to SystemMouseCursors.click.