I want to implement a CustomPainter that draws a clock given images of hour, minute, and second hands, as well as an image of an empty clock face. To account for the possibility of the image being too large or small, I used drawImageRect to resize it to a specific size before drawing it. However, when I drew a second hand on an empty clock as an experiment, I couldn't draw a second hand. I thought the image of the second hand was the problem, so I tried using canvas.drawCircle, but this was also not drawn. How can I draw the hands of a clock on an empty clock?
Below is the code I used. I used the image stored in the asset, and converted to ui.Image and imported to this page.
class CustomClockPage extends StatefulWidget {
final ui.Image clockImage;
final ui.Image secondImage;
const CustomClockPage({
required this.clockImage,
required this.secondImage,
Key? key,
}) : super(key: key);
@override
State<CustomClockPage> createState() => _CustomClockPageState();
}
class _CustomClockPageState extends State<CustomClockPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Positioned(
left: 50,
top: 100,
child: CustomClock(
clockImage: widget.clockImage,
secondImage: widget.secondImage,
),
),
],
),
);
}
}
class CustomClock extends StatefulWidget {
final ui.Image clockImage;
final ui.Image secondImage;
const CustomClock({
required this.clockImage,
required this.secondImage,
Key? key,
}) : super(key: key);
@override
State<CustomClock> createState() => _CustomClockState();
}
class _CustomClockState extends State<CustomClock> {
late DateTime now;
@override
void initState() {
super.initState();
now = DateTime.now();
Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
now = DateTime.now();
});
});
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
height: ((MediaQuery.of(context).size.width / 2) / widget.clockImage.width) * widget.clockImage.height,
child: CustomPaint(
painter: WatchPainter(
now: now,
width: MediaQuery.of(context).size.width / 2,
clockImage: widget.clockImage,
secondImage: widget.secondImage,
),
),
);
}
}
class WatchPainter extends CustomPainter {
final DateTime now;
final double width;
final ui.Image clockImage;
final ui.Image secondImage;
WatchPainter({
required this.now,
required this.width,
required this.clockImage,
required this.secondImage,
});
@override
void paint(Canvas canvas, Size size) async {
final xCenter = size.width / 2;
final yCenter = size.height / 2;
final reduceRatio = width /clockImage.width.toDouble();
renderClock(canvas, size);
renderHands(canvas, size, xCenter, yCenter, reduceRatio);
}
renderClock(Canvas canvas, Size size) {
final imageSize = Size(clockImage.width.toDouble(), clockImage.height.toDouble());
final src = Rect.fromLTWH(0, 0, imageSize.width, imageSize.height);
final dst = Rect.fromLTWH(0, 0, size.width, size.height);
canvas.drawImageRect(clockImage, src, dst, Paint());
}
renderHands(Canvas canvas, Size size, double xCenter, double yCenter, double reduceRatio) {
final secondRotation = now.second * (2 * pi / 60);
canvas.save();
canvas.rotate(secondRotation);
final secondOriginSize = Size(secondImage.width.toDouble(), secondImage.height.toDouble());
final secondSrc = Rect.fromLTWH(yCenter, xCenter, secondOriginSize.width, secondOriginSize.height);
final secondDst = Rect.fromLTWH(yCenter, xCenter, (secondOriginSize.width*reduceRatio), (secondOriginSize.height*reduceRatio));
canvas.drawImageRect(secondImage, secondSrc, secondDst, Paint());
canvas.restore();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
I wondered if the image itself was a problem, so I drew a basic function like drawCircle on the image, but it was not drawn.