import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:mnemo_cards/theme/themes.dart'; class MnemoSlider extends StatelessWidget { final double value; final void Function(double)? onChanged; final double min; final double max; MnemoSlider({ required this.value, this.onChanged, this.min = 0.2, this.max = 2.0, super.key, }); @override Widget build(BuildContext context) { var v = this.value; return Container( decoration: BoxDecoration( border: Border.all(color: borderGray), borderRadius: BorderRadius.circular(12.0), ), padding: EdgeInsets.symmetric(vertical: 3, horizontal: 2.0), child: SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: Theme.of(context).scaffoldBackgroundColor, inactiveTrackColor: Theme.of(context).scaffoldBackgroundColor, thumbColor: progressBlue, thumbShape: _RectSliderThumbShape( enabledThumbRadius: 19.0, borderRadius: Radius.circular(10), textBuilder: (v) { final value = ((v * (max - min) + min) * 10).toInt(); if (value % 10 == 0) { return '${(value / 10).toStringAsFixed(0)}x'; } else { return (value / 10).toStringAsFixed(1); } }, ), trackShape: RoundedRectSliderTrackShape(), trackHeight: 1.h, overlayColor: Colors.transparent, overlappingShapeStrokeColor: Colors.transparent, overlayShape: RoundSliderOverlayShape(overlayRadius: 2.0), ), child: StatefulBuilder(builder: (context, setState) { return Slider( min: min, max: max, value: v, onChanged: (updated) { setState(() { v = updated; }); onChanged?.call((updated * 100).toInt() / 100.0); }, ); }), ), ); } } class _RectSliderThumbShape extends SliderComponentShape { /// Create a slider thumb that draws a Rect. const _RectSliderThumbShape({ this.enabledThumbRadius = 10.0, this.disabledThumbRadius, this.elevation = 1.0, this.pressedElevation = 6.0, this.borderRadius, this.textBuilder, }); final String Function(double v)? textBuilder; /// The preferred radius of the round thumb shape when the slider is enabled. /// /// If it is not provided, then the Material Design default of 10 is used. final double enabledThumbRadius; /// The preferred radius of the round thumb shape when the slider is disabled. /// /// If no disabledRadius is provided, then it is equal to the /// [enabledThumbRadius] final double? disabledThumbRadius; final Radius? borderRadius; double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius; /// The resting elevation adds shadow to the unpressed thumb. /// /// The default is 1. /// /// Use 0 for no shadow. The higher the value, the larger the shadow. For /// example, a value of 12 will create a very large shadow. /// final double elevation; /// The pressed elevation adds shadow to the pressed thumb. /// /// The default is 6. /// /// Use 0 for no shadow. The higher the value, the larger the shadow. For /// example, a value of 12 will create a very large shadow. final double pressedElevation; @override Size getPreferredSize(bool isEnabled, bool isDiscrete) { return Size.fromRadius( isEnabled == true ? enabledThumbRadius : _disabledThumbRadius); } @override void paint( PaintingContext context, Offset center, { required Animation activationAnimation, required Animation enableAnimation, required bool isDiscrete, required TextPainter labelPainter, required RenderBox parentBox, required SliderThemeData sliderTheme, required TextDirection textDirection, required double value, required double textScaleFactor, required Size sizeWithOverflow, }) { final Canvas canvas = context.canvas; final Tween radiusTween = Tween( begin: _disabledThumbRadius, end: enabledThumbRadius, ); final ColorTween colorTween = ColorTween( begin: sliderTheme.disabledThumbColor, end: sliderTheme.thumbColor, ); final Color color = colorTween.evaluate(enableAnimation)!; final double radius = radiusTween.evaluate(enableAnimation); final Tween elevationTween = Tween( begin: elevation, end: pressedElevation, ); final double evaluatedElevation = elevationTween.evaluate(activationAnimation); final Path path = Path() ..addArc( Rect.fromCenter( center: center, width: 2 * radius, height: 2 * radius), 0, pi * 2); bool paintShadows = false; assert(() { if (debugDisableShadows) { paintShadows = false; } return true; }()); if (paintShadows) { canvas.drawShadow(path, Colors.black, evaluatedElevation, true); } final TextPainter textPainter = TextPainter(textDirection: TextDirection.rtl); textPainter.text = TextSpan( text: textBuilder?.call(value) ?? '', style: TextStyle( fontSize: radius, color: Colors.black, )); textPainter.layout(); final Offset textCenter = Offset(center.dx - (textPainter.width / 2), center.dy - (textPainter.height / 2)); // Use drawRect instead of drawCircle canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromCircle(center: center, radius: radius), borderRadius ?? Radius.zero, ), Paint()..color = borderGray, ); // Use drawRect instead of drawCircle canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromCircle(center: center, radius: radius - 1), borderRadius ?? Radius.zero, ), Paint()..color = color, ); textPainter.paint(canvas, textCenter); } }