I am trying to customize the slider widget to have a custom thumb shape.
In order to do this I have to extend SliderComponentShape
.
This class requires me to implement my own paint method.
The paint method only gives me a Canvas
to draw on.
Unfortunately the thumb shape I want to use is rather complex.
Drawing it manually would be rather tedious and building it with flutters Widgets would be much easier.
Is there a way to paint a Widget
to a Canvas
?
The Canvas widget enables you to draw shapes like lines (in any orientation), circles, rectangle, etc. in your application. Canvas widget draws the shapes inside its view bounds, by using the shape configurations provided in the shapesData property.
You can use an image or vector drawable as a thumb image, and reduce the painting complexity.
For example to get a map slider as below
You can use below code, and use the image file as thumb icon
class DistanceSlider extends StatefulWidget {
const DistanceSlider({
Key key,
@required this.imageUrl,
@required this.minDistance,
@required this.maxDistance,
}) : super(key: key);
final String imageUrl;
final double maxDistance;
final double minDistance;
@override
_DistanceSliderState createState() => _DistanceSliderState();
}
class _DistanceSliderState extends State<DistanceSlider> {
double distance = 20;
ui.Image customImage;
Future<ui.Image> loadImage(String imageUrl) async {
ByteData data = await rootBundle.load(imageUrl);
ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
ui.FrameInfo fi = await codec.getNextFrame();
return fi.image;
}
@override
void initState() {
loadImage(widget.imageUrl).then((image) {
setState(() {
customImage = image;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
child: SliderTheme(
data: SliderThemeData(
trackHeight: 8,
thumbShape: SliderThumbImage(
image: customImage,
thumbRadius: 0,
max: widget.maxDistance.toInt()),
overlayColor: kSecondaryColorLight.withAlpha(32),
overlayShape: RoundSliderOverlayShape(overlayRadius: 28.0),
activeTrackColor: kSecondaryColorDark,
inactiveTrackColor: kSecondaryColorLight.withOpacity(0.5),
valueIndicatorShape: PaddleSliderValueIndicatorShape(),
valueIndicatorTextStyle: TextStyle(
color: Colors.white,
),
valueIndicatorColor: Colors.white,
),
child: Stack(
children: <Widget>[
Slider(
label: distance.abs().toString(),
value: distance,
min: widget.minDistance,
max: widget.maxDistance,
onChanged: (value) {
setState(() {
distance = value;
});
},
),
Container(
margin: EdgeInsets.only(left: 25, right: 25, top: 40),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(widget.minDistance.toInt().toString()),
Text(widget.maxDistance.toInt().toString())
],
),
),
],
),
),
);
}
}
class SliderThumbImage extends SliderComponentShape {
final ui.Image image;
final double thumbRadius;
final int max;
SliderThumbImage({this.image, this.thumbRadius, this.max});
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return Size.fromRadius(thumbRadius);
}
@override
void paint(PaintingContext context, Offset center,
{Animation<double> activationAnimation,
Animation<double> enableAnimation,
bool isDiscrete,
TextPainter labelPainter,
RenderBox parentBox,
SliderThemeData sliderTheme,
TextDirection textDirection,
double value}) {
final canvas = context.canvas;
final imageWidth = image?.width ?? 10;
final imageHeight = image?.height ?? 10;
Offset imageOffset = Offset(
center.dx - (imageWidth / 2),
center.dy - (imageHeight / 0.935),
);
Paint paint = Paint()..filterQuality = FilterQuality.high;
if (image != null) {
canvas.drawImage(image, imageOffset, paint);
}
TextSpan span = new TextSpan(
style: new TextStyle(
fontSize: imageHeight * .3,
fontWeight: FontWeight.w700,
color: sliderTheme.valueIndicatorColor,
height: 0.9),
text: '${getValue(value)}');
TextPainter tp = new TextPainter(
text: span,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr);
tp.layout();
Offset textCenter = Offset(
center.dx - (tp.width / 2),
center.dy - (tp.height / 0.32),
);
tp.paint(canvas, textCenter);
}
String getValue(double value) {
return ((max * value).round()).toString();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With