mnemo_cards/lib/widgets/slider.dart

209 lines
6.1 KiB
Dart
Raw Normal View History

2024-06-15 15:42:18 +00:00
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: 1.0, horizontal: 2.0),
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.white,
inactiveTrackColor: Colors.white,
thumbColor: progressBlue,
thumbShape: _RectSliderThumbShape(
enabledThumbRadius: 18.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: 36.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<double> activationAnimation,
required Animation<double> 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<double> radiusTween = Tween<double>(
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<double> elevationTween = Tween<double>(
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);
}
}