This is based on the source code of sleek_circular_slider customized to be almost exactly the screenshot provided. I've included some new properties like touchWidth, innerTrackWidth, outerTrackWidth, handlerSize and handlerWidth.
Check out the live demo on DartPad

Source code:
/*
Copyright (c) 2019 Mat Nuckowski
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _modifier(double value) => '${value.ceil().toInt()}';
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xff827c7e),
body: Center(
child: CircularSlider(
appearance: CircularSliderAppearance(
startAngle: 180,
angleRange: 180,
size: 300,
counterClockwise: true,
infoProperties: InfoProperties(modifier: _modifier),
customColors: CustomSliderColors(),
customWidths: CustomSliderWidths(
touchWidth: 60,
innerTrackWidth: 10,
outerTrackWidth: 4,
handlerSize: 20,
handlerWidth: 20,
),
),
min: 0,
max: 10,
initialValue: 6,
onChange: (value) {},
),
),
);
}
}
typedef OnChange = void Function(double value);
typedef InnerWidget = Widget Function(double percentage);
class CircularSlider extends StatefulWidget {
final double initialValue;
final double min;
final double max;
final CircularSliderAppearance appearance;
final OnChange? onChange;
final OnChange? onChangeStart;
final OnChange? onChangeEnd;
final InnerWidget? innerWidget;
static const defaultAppearance = CircularSliderAppearance();
double get angle =>
valueToAngle(initialValue, min, max, appearance.angleRange);
const CircularSlider({
Key? key,
this.initialValue = 50,
this.min = 0,
this.max = 100,
this.appearance = defaultAppearance,
this.onChange,
this.onChangeStart,
this.onChangeEnd,
this.innerWidget,
}) : assert(min <= max),
assert(initialValue >= min && initialValue <= max),
super(key: key);
@override
State<CircularSlider> createState() => _CircularSliderState();
}
class _CircularSliderState extends State<CircularSlider>
with SingleTickerProviderStateMixin {
bool _isHandlerSelected = false;
bool _animationInProgress = false;
_CurvePainter? _painter;
double? _oldWidgetAngle;
double? _oldWidgetValue;
double? _curAngle;
late double _startAngle;
late double _angleRange;
double? _selectedAngle;
double? _rotation;
SpinAnimationManager? _spinManager;
ValueChangedAnimationManager? _animationManager;
late int _appearanceHashCode;
bool get _interactionEnabled => (widget.onChangeEnd != null ||
widget.onChange != null && !widget.appearance.spinnerMode);
@override
void initState() {
super.initState();
_startAngle = widget.appearance.startAngle;
_angleRange = widget.appearance.angleRange;
_appearanceHashCode = widget.appearance.hashCode;
if (!widget.appearance.animationEnabled) return;
widget.appearance.spinnerMode ? _spin() : _animate();
}
@override
void didUpdateWidget(CircularSlider oldWidget) {
if (oldWidget.angle != widget.angle &&
_curAngle?.toStringAsFixed(4) != widget.angle.toStringAsFixed(4)) {
_animate();
}
super.didUpdateWidget(oldWidget);
}
void _animate() {
if (!widget.appearance.animationEnabled || widget.appearance.spinnerMode) {
_setupPainter();
_updateOnChange();
return;
}
_animationManager ??= ValueChangedAnimationManager(
tickerProvider: this,
minValue: widget.min,
maxValue: widget.max,
durationMultiplier: widget.appearance.animDurationMultiplier,
);
_animationManager!.animate(
initialValue: widget.initialValue,
angle: widget.angle,
oldAngle: _oldWidgetAngle,
oldValue: _oldWidgetValue,
valueChangedAnimation: ((double anim, bool animationCompleted) {
_animationInProgress = !animationCompleted;
setState(() {
if (!animationCompleted) {
_curAngle = anim;
// update painter and the on change closure
_setupPainter();
_updateOnChange();
}
});
}));
}
void _spin() {
_spinManager = SpinAnimationManager(
tickerProvider: this,
duration: Duration(milliseconds: widget.appearance.spinnerDuration),
spinAnimation: ((double anim1, anim2, anim3) {
setState(() {
_rotation = anim1;
_startAngle = math.pi * anim2;
_curAngle = anim3;
_setupPainter();
_updateOnChange();
});
}));
_spinManager!.spin();
}
@override
Widget build(BuildContext context) {
/// _setupPainter excution when _painter is null or appearance has changed.
if (_painter == null || _appearanceHashCode != widget.appearance.hashCode) {
_appearanceHashCode = widget.appearance.hashCode;
_setupPainter();
}
return RawGestureDetector(
gestures: <Type, GestureRecognizerFactory>{
_CustomPanGestureRecognizer:
GestureRecognizerFactoryWithHandlers<_CustomPanGestureRecognizer>(
() => _CustomPanGestureRecognizer(
onPanDown: _onPanDown,
onPanUpdate: _onPanUpdate,
onPanEnd: _onPanEnd,
),
(_CustomPanGestureRecognizer instance) {},
),
},
child: _buildRotatingPainter(
rotation: _rotation,
size: Size(widget.appearance.size, widget.appearance.size)));
}
@override
void dispose() {
_spinManager?.dispose();
_animationManager?.dispose();
super.dispose();
}
void _setupPainter({bool counterClockwise = false}) {
var defaultAngle = _curAngle ?? widget.angle;
if (_oldWidgetAngle != null) {
if (_oldWidgetAngle != widget.angle) {
_selectedAngle = null;
defaultAngle = widget.angle;
}
}
_curAngle = calculateAngle(
startAngle: _startAngle,
angleRange: _angleRange,
selectedAngle: _selectedAngle,
defaultAngle: defaultAngle,
counterClockwise: counterClockwise);
_painter = _CurvePainter(
startAngle: _startAngle,
angleRange: _angleRange,
angle: _curAngle! < 0.5 ? 0.5 : _curAngle!,
appearance: widget.appearance);
_oldWidgetAngle = widget.angle;
_oldWidgetValue = widget.initialValue;
}
void _updateOnChange() {
if (widget.onChange != null && !_animationInProgress) {
final value =
angleToValue(_curAngle!, widget.min, widget.max, _angleRange);
widget.onChange!(value);
}
}
Widget _buildRotatingPainter({double? rotation, required Size size}) {
if (rotation != null) {
return Transform(
transform: Matrix4.identity()..rotateZ((rotation) * 5 * math.pi / 6),
alignment: FractionalOffset.center,
child: _buildPainter(size: size));
}
return _buildPainter(size: size);
}
Widget _buildPainter({required Size size}) {
return CustomPaint(
painter: _painter,
child: SizedBox(
width: size.width,
height: size.height,
child: _buildChildWidget()));
}
Widget? _buildChildWidget() {
if (widget.appearance.spinnerMode) return null;
final value = angleToValue(_curAngle!, widget.min, widget.max, _angleRange);
final childWidget = widget.innerWidget != null
? widget.innerWidget!(value)
: SliderLabel(
value: value,
appearance: widget.appearance,
);
return childWidget;
}
void _onPanUpdate(Offset details) {
if (!_isHandlerSelected) return;
if (_painter?.center == null) return;
_handlePan(details, false);
}
void _onPanEnd(Offset details) {
_handlePan(details, true);
if (widget.onChangeEnd != null) {
widget.onChangeEnd!(
angleToValue(_curAngle!, widget.min, widget.max, _angleRange));
}
_isHandlerSelected = false;
}
void _handlePan(Offset details, bool isPanEnd) {
if (_painter?.center == null) return;
RenderBox renderBox = context.findRenderObject() as RenderBox;
var position = renderBox.globalToLocal(details);
final double touchWidth = widget.appearance.touchWidth >= 25.0
? widget.appearance.touchWidth
: 25.0;
if (isPointAlongCircle(
position, _painter!.center!, _painter!.radius, touchWidth)) {
_selectedAngle = coordinatesToRadians(_painter!.center!, position);
// setup painter with new angle values and update onChange
_setupPainter(counterClockwise: widget.appearance.counterClockwise);
_updateOnChange();
setState(() {});
}
}
bool _onPanDown(Offset details) {
if (_painter == null || _interactionEnabled == false) {
return false;
}
RenderBox renderBox = context.findRenderObject() as RenderBox;
var position = renderBox.globalToLocal(details);
final angleWithinRange = isAngleWithinRange(
startAngle: _startAngle,
angleRange: _angleRange,
touchAngle: coordinatesToRadians(_painter!.center!, position),
previousAngle: _curAngle,
counterClockwise: widget.appearance.counterClockwise);
if (!angleWithinRange) {
return false;
}
final double touchWidth = widget.appearance.touchWidth >= 25.0
? widget.appearance.touchWidth
: 25.0;
if (isPointAlongCircle(
position, _painter!.center!, _painter!.radius, touchWidth)) {
_isHandlerSelected = true;
if (widget.onChangeStart != null) {
widget.onChangeStart!(
angleToValue(_curAngle!, widget.min, widget.max, _angleRange));
}
_onPanUpdate(details);
} else {
_isHandlerSelected = false;
}
return _isHandlerSelected;
}
}
typedef SpinAnimation = void Function(
double animation1, double animation2, double animation3);
class SpinAnimationManager {
final TickerProvider tickerProvider;
final Duration duration;
final SpinAnimation spinAnimation;
SpinAnimationManager({
required this.spinAnimation,
required this.duration,
required this.tickerProvider,
});
late Animation<double> _animation1;
late Animation<double> _animation2;
late Animation<double> _animation3;
late AnimationController _animController;
void spin() {
_animController = AnimationController(
vsync: tickerProvider, duration: duration)
..addListener(() {
spinAnimation(_animation1.value, _animation2.value, _animation3.value);
})
..repeat();
_animation1 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
parent: _animController,
curve: const Interval(0.5, 1.0, curve: Curves.linear)));
_animation2 = Tween<double>(begin: -80.0, end: 100.0).animate(
CurvedAnimation(
parent: _animController,
curve: const Interval(0, 1.0, curve: Curves.linear)));
_animation3 = Tween(begin: 0.0, end: 360.0).animate(CurvedAnimation(
parent: _animController,
curve: const Interval(0.0, 1.0, curve: SpinnerCurve())));
}
void dispose() {
_animController.dispose();
}
}
class SpinnerCurve extends Curve {
const SpinnerCurve();
@override
double transform(double t) => (t <= 0.5) ? 1.9 * t : 1.85 * (1 - t);
}
typedef ValueChangeAnimation = void Function(
double animation, bool animationFinished);
class ValueChangedAnimationManager {
final TickerProvider tickerProvider;
final double durationMultiplier;
final double minValue;
final double maxValue;
ValueChangedAnimationManager({
required this.tickerProvider,
required this.minValue,
required this.maxValue,
this.durationMultiplier = 1.0,
});
late Animation<double> _animation;
late final AnimationController _animController =
AnimationController(vsync: tickerProvider);
bool _animationCompleted = false;
void animate(
{required double initialValue,
double? oldValue,
required double angle,
double? oldAngle,
required ValueChangeAnimation valueChangedAnimation}) {
_animationCompleted = false;
final duration = (durationMultiplier *
valueToDuration(
initialValue, oldValue ?? minValue, minValue, maxValue))
.toInt();
_animController.duration = Duration(milliseconds: duration);
final curvedAnimation = CurvedAnimation(
parent: _animController,
curve: Curves.easeOut,
);
_animation =
Tween<double>(begin: oldAngle ?? 0, end: angle).animate(curvedAnimation)
..addListener(() {
valueChangedAnimation(_animation.value, _animationCompleted);
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationCompleted = true;
_animController.reset();
}
});
_animController.forward();
}
void dispose() {
_animController.dispose();
}
}
class _CurvePainter extends CustomPainter {
final double angle;
final CircularSliderAppearance appearance;
final double startAngle;
final double angleRange;
Offset? handler;
Offset? center;
late double radius;
_CurvePainter(
{required this.appearance,
this.angle = 30,
required this.startAngle,
required this.angleRange});
@override
void paint(Canvas canvas, Size size) {
radius =
math.min(size.width / 2, size.height / 2) - appearance.touchWidth * 0.5;
center = Offset(size.width / 2, size.height / 2);
final trackPaint = Paint()
..style = PaintingStyle.stroke
..color = appearance.outerTrackColor
..strokeWidth = appearance.outerTrackWidth;
drawCircularArc(
canvas: canvas,
size: Size(size.width, size.height),
paint: trackPaint,
radius: radius +
appearance.innerTrackWidth / 2 +
appearance.outerTrackWidth / 2,
ignoreAngle: true,
spinnerMode: appearance.spinnerMode);
trackPaint.color = appearance.innerTrackColor;
trackPaint.strokeWidth = appearance.innerTrackWidth;
drawCircularArc(
canvas: canvas,
size: size,
paint: trackPaint,
radius: radius,
ignoreAngle: true,
spinnerMode: appearance.spinnerMode);
final currentAngle = appearance.counterClockwise ? -angle : angle;
var dividersPaint = Paint()
..color = Colors.white
..strokeWidth = 1.5;
for (final angle in [0, 18, 36, 54, 72, 108, 126, 144, 162, 180]) {
Offset handler = degreesToCoordinates(center!, -math.pi / 2 + angle + 1.5,
radius - appearance.innerTrackWidth / 2 + 2);
Offset handler2 = degreesToCoordinates(
center!,
-math.pi / 2 + angle + 1.5,
radius + appearance.innerTrackWidth / 2 - 2);
canvas.drawLine(handler, handler2, dividersPaint);
}
var dotPaint = Paint();
Offset dotHandler =
degreesToCoordinates(center!, -math.pi / 2 + 90 + 1.5, radius);
dotPaint.shader = ui.Gradient.radial(
dotHandler,
appearance.innerTrackWidth / 3,
[const Color(0xfff963b5), const Color(0xfffe94cd)]);
canvas.drawCircle(dotHandler, appearance.innerTrackWidth / 3, dotPaint);
Offset handler = degreesToCoordinates(
center!, -math.pi / 2 + startAngle + currentAngle + 1.5, radius);
Offset handler2 = degreesToCoordinates(
center!,
-math.pi / 2 + startAngle + currentAngle + 1.5,
radius + appearance.handlerWidth);
var handlerPaint = Paint()
..shader = ui.Gradient.linear(handler, handler2,
[const Color(0xfffbf9fa), const Color(0xffff7ba3)]);
final path = Path()
..moveTo(handler2.dx, handler2.dy)
..arcTo(
Rect.fromCenter(
center: handler2,
width: appearance.handlerSize,
height: appearance.handlerSize),
degreeToRadians(currentAngle) + math.pi / 2 - math.pi / 4,
math.pi + math.pi / 2,
true,
)
..lineTo(handler.dx, handler.dy)
..close();
canvas.drawPath(path, handlerPaint);
}
drawCircularArc({
required Canvas canvas,
required Size size,
required Paint paint,
required double radius,
bool ignoreAngle = false,
bool spinnerMode = false,
}) {
final double angleValue = ignoreAngle ? 0 : (angleRange - angle);
final range = appearance.counterClockwise ? -angleRange : angleRange;
final currentAngle = appearance.counterClockwise ? angleValue : -angleValue;
canvas.drawArc(
Rect.fromCircle(center: center!, radius: radius),
degreeToRadians(spinnerMode ? 0 : startAngle),
degreeToRadians(spinnerMode ? 360 : range + currentAngle),
false,
paint,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
double degreeToRadians(double degree) => (math.pi / 180) * degree;
double radiansToDegrees(double radians) => radians * (180 / math.pi);
Offset degreesToCoordinates(Offset center, double degrees, double radius) =>
radiansToCoordinates(center, degreeToRadians(degrees), radius);
Offset radiansToCoordinates(Offset center, double radians, double radius) {
var dx = center.dx + radius * math.cos(radians);
var dy = center.dy + radius * math.sin(radians);
return Offset(dx, dy);
}
double coordinatesToRadians(Offset center, Offset coords) {
var a = coords.dx - center.dx;
var b = center.dy - coords.dy;
return radiansNormalized(math.atan2(b, a));
}
double radiansNormalized(double radians) {
var normalized = radians < 0 ? -radians : 2 * math.pi - radians;
return normalized;
}
bool isPointInsideCircle(Offset point, Offset center, double rradius) {
var radius = rradius * 1.2;
return point.dx < (center.dx + radius) &&
point.dx > (center.dx - radius) &&
point.dy < (center.dy + radius) &&
point.dy > (center.dy - radius);
}
bool isPointAlongCircle(
Offset point, Offset center, double radius, double width) {
var dx = math.pow(point.dx - center.dx, 2);
var dy = math.pow(point.dy - center.dy, 2);
var distance = math.sqrt(dx + dy);
return (distance - radius).abs() < width;
}
double calculateRawAngle({
required double startAngle,
required double angleRange,
required double selectedAngle,
bool counterClockwise = false,
}) {
double angle = radiansToDegrees(selectedAngle);
if (!counterClockwise) {
if (angle >= startAngle && angle <= 360.0) return angle - startAngle;
return 360.0 - startAngle + angle;
}
if (angle <= startAngle) return startAngle - angle;
return 360.0 - angle + startAngle;
}
double calculateAngle({
required double startAngle,
required double angleRange,
required selectedAngle,
required defaultAngle,
bool counterClockwise = false,
}) {
if (selectedAngle == null) return defaultAngle;
double calcAngle = calculateRawAngle(
startAngle: startAngle,
angleRange: angleRange,
selectedAngle: selectedAngle,
counterClockwise: counterClockwise,
);
if (calcAngle - angleRange > (360.0 - angleRange) * 0.5) return 0.0;
if (calcAngle > angleRange) return angleRange;
return calcAngle;
}
bool isAngleWithinRange({
required double startAngle,
required double angleRange,
required touchAngle,
required previousAngle,
bool counterClockwise = false,
}) {
double calcAngle = calculateRawAngle(
startAngle: startAngle,
angleRange: angleRange,
selectedAngle: touchAngle,
counterClockwise: counterClockwise);
return !(calcAngle > angleRange);
}
int valueToDuration(double value, double previous, double min, double max) {
final divider = (max - min) / 100;
return divider != 0 ? (value - previous).abs() ~/ divider * 15 : 0;
}
double valueToPercentage(double value, double min, double max) =>
value / ((max - min) / 100);
double valueToAngle(double value, double min, double max, double angleRange) =>
percentageToAngle(valueToPercentage(value - min, min, max), angleRange);
double percentageToValue(double percentage, double min, double max) =>
((max - min) / 100) * percentage + min;
double percentageToAngle(double percentage, double angleRange) {
final step = angleRange / 100;
if (percentage > 100) return angleRange;
if (percentage < 0) return 0.5;
return percentage * step;
}
double angleToValue(double angle, double min, double max, double angleRange) {
return percentageToValue(angleToPercentage(angle, angleRange), min, max);
}
double angleToPercentage(double angle, double angleRange) {
final step = angleRange / 100;
if (angle > angleRange) return 100;
if (angle < 0.5) return 0;
return angle / step;
}
class _CustomPanGestureRecognizer extends OneSequenceGestureRecognizer {
final Function onPanDown;
final Function onPanUpdate;
final Function onPanEnd;
_CustomPanGestureRecognizer({
required this.onPanDown,
required this.onPanUpdate,
required this.onPanEnd,
});
@override
void addPointer(PointerEvent event) {
if (onPanDown(event.position)) {
startTrackingPointer(event.pointer);
resolve(GestureDisposition.accepted);
} else {
stopTrackingPointer(event.pointer);
}
}
@override
void handleEvent(PointerEvent event) {
if (event is PointerMoveEvent) {
onPanUpdate(event.position);
}
if (event is PointerUpEvent) {
onPanEnd(event.position);
stopTrackingPointer(event.pointer);
}
}
@override
String get debugDescription => 'customPan';
@override
void didStopTrackingLastPointer(int pointer) {}
}
class SliderLabel extends StatelessWidget {
final double value;
final CircularSliderAppearance appearance;
const SliderLabel({Key? key, required this.value, required this.appearance})
: super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: builtInfo(appearance),
);
}
List<Widget> builtInfo(CircularSliderAppearance appearance) {
var widgets = <Widget>[];
if (appearance.infoTopLabelText != null) {
widgets.add(Text(
appearance.infoTopLabelText!,
style: appearance.infoTopLabelStyle,
));
}
final modifier = appearance.infoModifier(value);
widgets.add(
Text(modifier, style: appearance.infoMainLabelStyle),
);
if (appearance.infoBottomLabelText != null) {
widgets.add(Text(
appearance.infoBottomLabelText!,
style: appearance.infoBottomLabelStyle,
));
}
return widgets;
}
}
typedef PercentageModifier = String Function(double percentage);
class CircularSliderAppearance {
static const double _defaultSize = 150.0;
static const double _defaultStartAngle = 150.0;
static const double _defaultAngleRange = 240.0;
static const Color _defaultInnerTrackColor = Colors.black;
static const Color _defaultOuterTrackColor = Colors.white;
String _defaultPercentageModifier(double value) =>
'${value.ceil().toInt()} %';
final double size;
final double startAngle;
final double angleRange;
final bool animationEnabled;
final bool spinnerMode;
final bool counterClockwise;
final double animDurationMultiplier;
final int spinnerDuration;
final CustomSliderWidths? customWidths;
final CustomSliderColors? customColors;
final InfoProperties? infoProperties;
double? get _customInnerTrackWidth => customWidths?.innerTrackWidth;
double? get _customOuterTrackWidth => customWidths?.outerTrackWidth;
double? get _customTouchWidth => customWidths?.touchWidth;
double? get _customHandlerSize => customWidths?.handlerSize;
double? get _customHandlerWidth => customWidths?.handlerWidth;
double get innerTrackWidth => _customInnerTrackWidth ?? touchWidth / 4.0;
double get outerTrackWidth => _customOuterTrackWidth ?? touchWidth / 4.0;
double get touchWidth => _customTouchWidth ?? size / 10.0;
double get handlerSize => _customHandlerSize ?? touchWidth / 5.0;
double get handlerWidth => _customHandlerWidth ?? touchWidth / 5.0;
Color? get _customInnerTrackColor => customColors?.innerTrackColor;
Color? get _customOuterTrackColor => customColors?.outerTrackColor;
Color get innerTrackColor =>
_customInnerTrackColor ?? _defaultInnerTrackColor;
Color get outerTrackColor =>
_customOuterTrackColor ?? _defaultOuterTrackColor;
String? get _topLabelText => infoProperties?.topLabelText;
String? get _bottomLabelText => infoProperties?.bottomLabelText;
TextStyle? get _mainLabelStyle => infoProperties?.mainLabelStyle;
TextStyle? get _topLabelStyle => infoProperties?.topLabelStyle;
TextStyle? get _bottomLabelStyle => infoProperties?.bottomLabelStyle;
PercentageModifier? get _modifier => infoProperties?.modifier;
PercentageModifier get infoModifier =>
_modifier ?? _defaultPercentageModifier;
String? get infoTopLabelText => _topLabelText;
String? get infoBottomLabelText => _bottomLabelText;
TextStyle get infoMainLabelStyle =>
_mainLabelStyle ??
TextStyle(
fontWeight: FontWeight.w100,
fontSize: size / 5.0,
color: const Color.fromRGBO(30, 0, 59, 1.0));
TextStyle get infoTopLabelStyle =>
_topLabelStyle ??
TextStyle(
fontWeight: FontWeight.w600,
fontSize: size / 10.0,
color: const Color.fromRGBO(147, 81, 120, 1.0));
TextStyle get infoBottomLabelStyle =>
_bottomLabelStyle ??
TextStyle(
fontWeight: FontWeight.w600,
fontSize: size / 10.0,
color: const Color.fromRGBO(147, 81, 120, 1.0));
const CircularSliderAppearance({
this.customWidths,
this.customColors,
this.size = _defaultSize,
this.startAngle = _defaultStartAngle,
this.angleRange = _defaultAngleRange,
this.infoProperties,
this.animationEnabled = true,
this.counterClockwise = false,
this.spinnerMode = false,
this.spinnerDuration = 1500,
this.animDurationMultiplier = 1.0,
});
}
class CustomSliderWidths {
final double? innerTrackWidth;
final double? outerTrackWidth;
final double? touchWidth;
final double? handlerSize;
final double? handlerWidth;
CustomSliderWidths({
this.innerTrackWidth,
this.outerTrackWidth,
this.touchWidth,
this.handlerSize,
this.handlerWidth,
});
}
class CustomSliderColors {
final Color? innerTrackColor;
final Color? outerTrackColor;
CustomSliderColors({
this.innerTrackColor,
this.outerTrackColor,
});
}
class InfoProperties {
final PercentageModifier? modifier;
final TextStyle? mainLabelStyle;
final TextStyle? topLabelStyle;
final TextStyle? bottomLabelStyle;
final String? topLabelText;
final String? bottomLabelText;
InfoProperties({
this.topLabelText,
this.bottomLabelText,
this.mainLabelStyle,
this.topLabelStyle,
this.bottomLabelStyle,
this.modifier,
});
}