diff --git a/example/lib/pages/slider.dart b/example/lib/pages/slider.dart index 8476d07..09e3d0c 100644 --- a/example/lib/pages/slider.dart +++ b/example/lib/pages/slider.dart @@ -9,20 +9,34 @@ class SliderExample extends StatefulWidget { class _SliderExampleState extends State { double value = 0.5; + double value2 = 0.5; @override Widget build(BuildContext context) { - return Center( - child: Padding( - padding: const EdgeInsets.all(32.0), - child: ZenitSlider( - onChanged: (val) { - setState(() { - value = val; - }); - }, - value: value, - ), + return Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ZenitSlider( + divisions: 10, + onChanged: (val) { + setState(() { + value = val; + }); + }, + value: value, + ), + const SizedBox(height: 32), + ZenitSlider( + onChanged: (val) { + setState(() { + value2 = val; + }); + }, + value: value2, + ), + ], ), ); } diff --git a/lib/src/components/slider/slider.dart b/lib/src/components/slider/slider.dart index 2340883..601c523 100644 --- a/lib/src/components/slider/slider.dart +++ b/lib/src/components/slider/slider.dart @@ -5,23 +5,21 @@ class ZenitSlider extends StatefulWidget { final double value; final ValueChanged onChanged; - final Color? activeColor; - // TODO: implement divisions - //final int? divisions; - final Color? trackColor; + final int? divisions; final MouseCursor? mouseCursor; - final Color? thumbColor; + final ZenitSliderTheme? sliderTheme; const ZenitSlider({ super.key, required this.value, required this.onChanged, - this.activeColor, - //this.divisions, - this.trackColor, + this.divisions, this.mouseCursor, - this.thumbColor, - }) : assert(value >= 0.0 && value <= 1.0); + this.sliderTheme, + }) : assert( + value >= 0.0 && value <= 1.0 && divisions == null || + divisions != null && divisions > 0 && divisions <= 100, + ); @override State createState() => _ZenitSliderState(); @@ -29,10 +27,12 @@ class ZenitSlider extends StatefulWidget { class _ZenitSliderState extends State { bool hover = false; + @override Widget build(BuildContext context) { - final sliderTheme = ZenitTheme.sliderTheme(context); + final sliderTheme = widget.sliderTheme ?? ZenitTheme.sliderTheme(context); double newValue = widget.value; + final double divisionsWidthRatio = 1 / (widget.divisions ?? 1); return LayoutBuilder( builder: (context, constraints) { return MouseRegion( @@ -42,28 +42,37 @@ class _ZenitSliderState extends State { child: Listener( onPointerPanZoomStart: (details) { newValue = details.localPosition.dx / (constraints.maxWidth); - widget.onChanged(newValue); + if (widget.divisions != null) { + newValue = (newValue / divisionsWidthRatio).round() * divisionsWidthRatio; + } + if (newValue >= 0.0 && newValue <= 1.0) widget.onChanged(newValue); }, child: GestureDetector( onTapDown: (details) { newValue = details.localPosition.dx / (constraints.maxWidth); - widget.onChanged(newValue); + if (widget.divisions != null) { + newValue = (newValue / divisionsWidthRatio).round() * divisionsWidthRatio; + } + if (newValue >= 0.0 && newValue <= 1.0) widget.onChanged(newValue); }, onHorizontalDragUpdate: (details) { - newValue += details.delta.dx / constraints.maxWidth; - if (newValue >= 0.0 && newValue <= 1.0) { - widget.onChanged(newValue); + newValue = details.localPosition.dx / (constraints.maxWidth); + if (widget.divisions != null) { + newValue = (newValue / divisionsWidthRatio).round() * divisionsWidthRatio; } + if (newValue >= 0.0 && newValue <= 1.0) widget.onChanged(newValue); }, child: CustomPaint( painter: _SliderPainter( - trackColor: widget.trackColor ?? sliderTheme.trackColor, - activeColor: widget.activeColor ?? sliderTheme.activeTrackColor, - thumbColor: widget.thumbColor ?? sliderTheme.thumbColor, + trackColor: sliderTheme.trackColor, + activeColor: sliderTheme.activeTrackColor, + thumbColor: sliderTheme.thumbColor, hover: hover, hoverColor: Theme.of(context).colorScheme.onSurface.withOpacity(0.05), outlineColor: sliderTheme.outlineColor, value: widget.value, + dividerColor: sliderTheme.dividerColor, + divisions: widget.divisions, ), size: Size(constraints.maxWidth, 48), ), @@ -83,6 +92,8 @@ class _SliderPainter extends CustomPainter { final Color hoverColor; final Color thumbColor; final Color outlineColor; + final Color dividerColor; + final int? divisions; _SliderPainter({ required this.trackColor, @@ -92,6 +103,8 @@ class _SliderPainter extends CustomPainter { required this.hoverColor, required this.thumbColor, required this.outlineColor, + required this.dividerColor, + this.divisions, }); @override @@ -113,25 +126,49 @@ class _SliderPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeWidth = 1; - final RRect track = RRect.fromLTRBR(0.0, 12.0, size.width, size.height - 12, const Radius.circular(12.0)); - final RRect active = RRect.fromLTRBR( - 0.0, - 12.0, - (size.width * value) > 24 ? (size.width * value) : 24.0, - size.height - 12.0, - const Radius.circular(12.0), - ); + final Paint dividerPaint = Paint() + ..color = dividerColor + ..style = PaintingStyle.fill; - final Offset thumbPosition = Offset( - (size.width * value) > 24 ? (size.width * value) - ((size.height - 26) / 3) - 6 : 24 - 12, - size.height / 2, - ); + double activeWidth() { + if (size.width * value >= size.width) { + return size.width; + } else if (size.width * value < 24) { + return 24; + } else { + return size.width * value + 12; + } + } + + double thumbPositionX() { + if (size.width * value >= size.width) { + return size.width - 12; + } else if (size.width * value < 24) { + return 12; + } else { + return size.width * value; + } + } + + const kTrackBorderRadius = Radius.circular(12.0); + + final RRect track = RRect.fromLTRBR(0.0, 12.0, size.width, size.height - 12, kTrackBorderRadius); + final RRect active = RRect.fromLTRBR(0.0, 12.0, activeWidth(), size.height - 12.0, kTrackBorderRadius); + + final Offset thumbPosition = Offset(thumbPositionX(), size.height / 2); canvas.drawRRect(track, trackPaint); canvas.drawRRect(track, outlinePaint); canvas.drawRRect(active, activePaint); + final int divisions = this.divisions ?? 1; + for (int i = 0; i < divisions - 1; i++) { + final double x = (size.width / divisions) * i + (size.width / divisions); + final Offset position = Offset(x, size.height / 2); + canvas.drawCircle(position, 2, dividerPaint); + } + if (hover) { final Paint hoverPaint = Paint() ..color = hoverColor @@ -151,6 +188,7 @@ class _SliderPainter extends CustomPainter { old.hover != hover || old.hoverColor != hoverColor || old.thumbColor != thumbColor || - old.outlineColor != outlineColor; + old.outlineColor != outlineColor || + old.divisions != divisions; } } diff --git a/lib/src/theme/theme.dart b/lib/src/theme/theme.dart index f835fc8..0d56c55 100644 --- a/lib/src/theme/theme.dart +++ b/lib/src/theme/theme.dart @@ -28,6 +28,7 @@ mixin ZenitTheme { trackColor: theme.colorScheme.surface, thumbColor: theme.colorScheme.onPrimary, outlineColor: theme.colorScheme.outline, + dividerColor: theme.colorScheme.outline, ); } @@ -262,12 +263,14 @@ class ZenitSliderTheme { final Color trackColor; final Color outlineColor; final Color thumbColor; + final Color dividerColor; const ZenitSliderTheme({ required this.activeTrackColor, required this.trackColor, required this.outlineColor, required this.thumbColor, + required this.dividerColor, }); ZenitSliderTheme copyWith({ @@ -275,12 +278,14 @@ class ZenitSliderTheme { Color? trackColor, Color? outlineColor, Color? thumbColor, + Color? dividerColor, }) { return ZenitSliderTheme( activeTrackColor: activeTrackColor ?? this.activeTrackColor, trackColor: trackColor ?? this.trackColor, outlineColor: outlineColor ?? this.outlineColor, thumbColor: thumbColor ?? this.thumbColor, + dividerColor: dividerColor ?? this.dividerColor, ); } }