I'm working on a custom slider widget in Flutter where I need the slider track to change its thickness slightly in an increasing way. I have not found any built-in properties in the Slider
widget that could help achieve this effect. The thickness should increase as it gets closer to the end and decrease as it moves towards the start.
I tried to create a custom SliderTheme
and change the trackShape
, here's the relevant code:
class CustomSliderTrackShape extends RoundedRectSliderTrackShape {
final double overlayWidth;
const CustomSliderTrackShape({
this.overlayWidth = 30.0,
});
@override
Rect getPreferredRect({
required RenderBox parentBox,
Offset offset = Offset.zero,
required SliderThemeData sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
final double overlayRadius = overlayWidth / 2;
final double trackLeft = offset.dx + overlayRadius;
final double trackTop = offset.dy + (parentBox.size.height / 2);
final double trackHeight = sliderTheme.trackHeight!;
// --> not sure how to increase the width of the track
final double trackWidth = parentBox.size.width - (2 * overlayRadius) + 10;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
}
And it's usage:
SliderTheme(
data: SliderTheme.of(context).copyWith(
// --> Using it here.
trackShape: CustomSliderTrackShape(),
),
child: Slider(
value: 0.4,
onChanged: (newValue) {},
),
)
which results in (seems like the regular Slider
):
However, the desired output is:
How can I modify the getPreferredRect
function to create a custom slider that increases its height/thickness as it comes to the end? should I be using CustomPaint
instead in order to achieve a slider with varying track thickness?
Here's the code as a fully runnable snippet:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Custom Slider Example')),
body: MySlider(),
),
);
}
}
class MySlider extends StatefulWidget {
@override
_MySliderState createState() => _MySliderState();
}
class _MySliderState extends State<MySlider> {
double _value = 50;
@override
Widget build(BuildContext context) {
return SliderTheme(
data: SliderTheme.of(context).copyWith(
trackHeight: 6,
trackShape: CustomSliderTrackShape(),
),
child: Slider(
min: 0,
max: 100,
divisions: 100,
value: _value,
onChanged: (value) {
setState(() {
_value = value;
});
},
),
);
}
}
class CustomSliderTrackShape extends RoundedRectSliderTrackShape {
final double overlayWidth;
const CustomSliderTrackShape({
this.overlayWidth = 30.0,
});
@override
Rect getPreferredRect({
required RenderBox parentBox,
Offset offset = Offset.zero,
required SliderThemeData sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
final double overlayRadius = overlayWidth / 2;
final double trackLeft = offset.dx + overlayRadius;
final double trackTop = offset.dy + (parentBox.size.height / 2);
final double trackHeight = sliderTheme.trackHeight!;
// --> not sure how to increase the width of the track
final double trackWidth = parentBox.size.width - (2 * overlayRadius) + 10;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
}
You can play with Paint
, hope this help. Also try to find better ratio for your case,
class CustomSliderTrackShape extends RoundedRectSliderTrackShape {
@override
void paint(
PaintingContext context,
Offset offset, {
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required Animation<double> enableAnimation,
required TextDirection textDirection,
required Offset thumbCenter,
Offset? secondaryOffset,
bool isDiscrete = false,
bool isEnabled = false,
double additionalActiveTrackHeight = 2,
}) {
final Rect trackRect = getPreferredRect(
parentBox: parentBox,
offset: offset,
sliderTheme: sliderTheme,
isEnabled: isEnabled,
isDiscrete: isDiscrete,
);
final radius = trackRect.height * .3; //todo: use your trackHeightRatio
final thumbWidth =
sliderTheme.thumbShape!.getPreferredSize(isEnabled, isDiscrete).width;
final Path path = Path()
..moveTo(
radius + trackRect.left, trackRect.top + trackRect.height / 2) //lt
..lineTo(trackRect.width + thumbWidth, trackRect.top) //rt
..arcToPoint(
//rb
Offset(trackRect.width + thumbWidth, trackRect.bottom),
radius: Radius.circular(radius),
)
..lineTo(trackRect.left, trackRect.bottom - radius) //lb
..arcToPoint(
//lt
Offset(trackRect.left, trackRect.top + trackRect.height / 2),
radius: Radius.circular(radius),
)
..close();
final Paint trackPaint = Paint()..color = Colors.grey.shade300;
context.canvas.drawPath(path, trackPaint);
}
}