208 lines
6 KiB
Dart
208 lines
6 KiB
Dart
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: Colors.white,
|
|
inactiveTrackColor: Colors.white,
|
|
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<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);
|
|
}
|
|
}
|