PrimitiveSlider
A slider component for selecting a value from a range. It demonstrates how to handle continuous drag gestures and map them to a value range, rendering the result with custom painting.
Overview
PrimitiveSlider provides a range selection interface using only primitive Flutter components. It tracks gestures to update a value and repaints the slider track and thumb accordingly.
Primitives Used: CustomPaint, Canvas, GestureDetector
Basic Usage
import 'package:primitive_ui/primitive_ui.dart';
double _value = 0.5;
PrimitiveSlider(
value: _value,
onChanged: (newValue) {
setState(() {
_value = newValue;
});
},
)API Reference
Constructor
PrimitiveSlider({
Key? key,
required double value,
ValueChanged<double>? onChanged,
double min = 0.0,
double max = 1.0,
Color activeColor = const Color(0xFF2196F3),
Color inactiveColor = const Color(0xFFE0E0E0),
Color thumbColor = const Color(0xFFFFFFFF),
double thumbRadius = 10.0,
double trackHeight = 4.0,
ValueChanged<double>? onChangeStart,
ValueChanged<double>? onChangeEnd,
String? semanticsLabel,
double? semanticsStep,
Duration duration = const Duration(milliseconds: 200),
Curve curve = Curves.easeInOut,
})Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
value | double | ✓ | — | The currently selected value for this slider |
onChanged | ValueChanged<double>? | ✗ | null | Called during a drag when the user is selecting a new value |
min | double | ✗ | 0.0 | The minimum value the user can select |
max | double | ✗ | 1.0 | The maximum value the user can select |
activeColor | Color | ✗ | Color(0xFF2196F3) | The color of the track up to the thumb |
inactiveColor | Color | ✗ | Color(0xFFE0E0E0) | The color of the track after the thumb |
thumbColor | Color | ✗ | Color(0xFFFFFFFF) | The color of the thumb (circle) |
thumbRadius | double | ✗ | 10.0 | The radius of the thumb shape |
trackHeight | double | ✗ | 4.0 | The thickness of the track |
onChangeStart | ValueChanged<double>? | ✗ | null | Called when the user starts selecting a new value |
onChangeEnd | ValueChanged<double>? | ✗ | null | Called when the user is done selecting a new value |
semanticsLabel | String? | ✗ | null | Accessibility label (defaults to "Slider") |
semanticsStep | double? | ✗ | null | Value step for keyboard actions (defaults to 10% of range) |
duration | Duration | ✗ | Duration(milliseconds: 200) | Animation duration for programmatic value changes |
curve | Curve | ✗ | Curves.easeInOut | Animation curve for programmatic value changes |
Examples
Custom Colors and Thumb
Standard
PrimitiveSlider(
value: _value,
onChanged: (v) => setState(() => _value = v),
)Handling Drag Events
PrimitiveSlider(
value: _sliderValue,
onChanged: (value) {
setState(() => _sliderValue = value);
},
onChangeStart: (value) {
print('Started dragging at $value');
},
onChangeEnd: (value) {
print('Finished dragging at $value');
},
)Implementation Details
The PrimitiveSlider is implemented using:
GestureDetector: SpecificallyonHorizontalDragUpdate,onHorizontalDragStart,onHorizontalDragEnd, andonTapDown. These callbacks provide the global position of the interaction.- Coordinate Mapping: The component converts the global touch position to a local position using
RenderBox.globalToLocal. It then calculates the percentage of the drag along the track width. CustomPaint: ACustomPainterdraws two lines (active and inactive track) and a circle (thumb) based on the current value.- Shadows: The thumb has a subtle shadow drawn using
BoxShadowpainting logic on the canvas.
Implicit Animations
The slider supports implicit animations for value changes:
- When
valuechanges programmatically (not via drag), it animates smoothly to the new position usingTweenAnimationBuilder. - During drag interactions, the animation duration is set to zero to ensure immediate responsiveness.
This allows for smooth “seek” behavior when tapping the track or updating the value from an external source, without sacrificing the direct feel of dragging.
This component demonstrates the core concept of “unidirectional data flow” in UI: the gesture updates the state, which triggers a rebuild, which passes the new value to the painter.
Limitations
Known Limitations:
- Width must be bounded by parent (e.g., inside a SizedBox or constrained column) or it defaults to 200.0.
- No support for divisions or discrete values yet.
Related Components
- PrimitiveCircularProgress - Another value indicator
- PrimitiveToggleSwitch - For boolean values
Source Code
View the complete implementation in the GitHub repository .