NakedSlider
Headless slider component with drag interactions and accessibility
Headless slider component. Handles drag interactions, keyboard navigation (arrow keys), and accessibility. Use builder pattern for custom styling.
When to use this
- Value selection: Let users pick numbers within a range (volume, brightness, price filters)
- Settings controls: Adjust app preferences with visual feedback
- Form inputs: Numeric input with immediate visual feedback
- Custom designs: When you need sliders that match your design system perfectly
See complete examples in our GitHub repository.
Basic implementation
import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';
class SimpleSlider extends StatefulWidget {
@override
State<SimpleSlider> createState() => _SimpleSliderState();
}
class _SimpleSliderState extends State<SimpleSlider> {
double _value = 0.5;
@override
Widget build(BuildContext context) {
return NakedSlider(
value: _value,
onChanged: (newValue) => setState(() => _value = newValue),
builder: (context, state, child) {
return Container(
width: 200,
height: 20,
child: Stack(
children: [
// Track
Container(
width: 200,
height: 4,
margin: const EdgeInsets.only(top: 8),
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(2),
),
),
// Progress
Container(
width: 200 * _value,
height: 4,
margin: const EdgeInsets.only(top: 8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(2),
),
),
// Thumb
Positioned(
left: (200 - 20) * _value,
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: state.isDragging ? Colors.blue.shade700 : Colors.blue,
shape: BoxShape.circle,
),
),
),
],
),
);
},
);
}
}
Multiple state styling
Handle hover, focus, and drag states:
builder: (context, state, child) {
// Different colors for different states
Color thumbColor = Colors.blue;
if (state.isDragging) thumbColor = Colors.blue.shade800;
else if (state.isHovered) thumbColor = Colors.blue.shade600;
// Focus indicator
final borderColor = state.isFocused ? Colors.orange : Colors.transparent;
return Stack(
children: [
// Your track containers here...
// Enhanced thumb with focus ring
Positioned(
left: (200 - 24) * _value,
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: thumbColor,
shape: BoxShape.circle,
border: Border.all(color: borderColor, width: 2),
// Add shadow when dragging
boxShadow: state.isDragging ? [
BoxShadow(
color: Colors.blue.withOpacity(0.3),
blurRadius: 8,
spreadRadius: 2,
),
] : null,
),
),
),
],
);
}
Volume control with icons
Row(
children: [
Icon(Icons.volume_down),
const SizedBox(width: 12),
Expanded(
child: NakedSlider(
value: _volume,
onChanged: (value) => setState(() => _volume = value),
builder: (context, state, child) {
// Your slider design with volume-specific styling
return YourSliderDesign();
},
),
),
const SizedBox(width: 12),
Icon(Icons.volume_up),
],
)
Discrete value slider
NakedSlider(
value: _rating,
min: 1,
max: 5,
divisions: 4, // Creates 5 discrete steps (1, 2, 3, 4, 5)
onChanged: (value) => setState(() => _rating = value.round().toDouble()),
builder: (context, state, child) {
// Show star rating or discrete indicators
return YourStarRating();
},
)
Custom painters (advanced)
For complex designs, you can use CustomPaint for pixel-perfect control. Check out our complete CustomPaint examples in the repository.
Constructor
const NakedSlider({
Key? key,
this.child,
this.builder,
required this.value,
this.min = 0.0,
this.max = 1.0,
this.onChanged,
this.onDragStart,
this.onDragEnd,
this.onHoverChange,
this.onDragChange,
this.onFocusChange,
this.enabled = true,
this.mouseCursor = SystemMouseCursors.click,
this.enableFeedback = true,
this.focusNode,
this.autofocus = false,
this.direction = Axis.horizontal,
this.divisions,
this.keyboardStep = 0.01,
this.largeKeyboardStep = 0.1,
this.semanticLabel,
})
builder → ValueWidgetBuilder<NakedSliderState>?
Optional builder that receives the current NakedSliderState
along with the child
. Use this for reactive styling without a StatefulWidget.
onHoverChange → ValueChanged<bool>?
Called when hover state changes. Provides the current hover state (true when hovered, false otherwise).
onDragChange → ValueChanged<bool>?
Called when dragging state changes. Provides the current dragging state (true when dragging, false otherwise).
onFocusChange → ValueChanged<bool>?
Called when focus state changes. Provides the current focus state (true when focused, false otherwise).
enabled → bool
Whether the slider is enabled. When true, the slider will respond to user interaction. Defaults to true.
direction → Axis
Direction of the slider. Can be horizontal (left to right) or vertical (bottom to top). Defaults to Axis.horizontal.
keyboardStep → double
Step size for keyboard navigation. This value is used when arrow keys are pressed to increment or decrement the slider value. Defaults to 0.01 (1% of the slider range).