import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
class ClockPage extends StatelessWidget {
const ClockPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Clock();
}
}
class Clock extends StatefulWidget {
const Clock({Key? key}) : super(key: key);
@override
_ClockState createState() => _ClockState();
}
class _ClockState extends State<Clock> {
// 定时器,一秒一次
late Timer _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {});
});
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
// AspectRatio 可以根据具体的长宽比约束 child 的布局范围, 从而影响 child 的大小. 通常在视频、图像中会经常使用
return AspectRatio(
aspectRatio: 1,
child: CustomPaint(
painter: ClockPainter(dateTime: DateTime.now()),
),
);
}
}
class ClockPainter extends CustomPainter {
final DateTime dateTime;
ClockPainter({super.repaint, required this.dateTime});
@override
void paint(Canvas canvas, Size size) {
final centerX = size.width / 2; // 计算画布中心点的X坐标
final centerY = size.height / 2; // 计算画布中心点的Y坐标
final center = Offset(centerX, centerY); // 画布中心点
final radius = min(centerX - 10, centerY); // 计算画布的半径,取宽和高中的最小值
final paint = Paint()
..strokeWidth = 10
..strokeCap = StrokeCap.round; // 边缘圆滑
// 画表盘
paint.color = Colors.black; // 设置画笔颜色为黑色
paint.style = PaintingStyle.stroke; // 设置画笔样式为描边
canvas.drawCircle(center, radius, paint); // 在画布上画一个圆形的表盘
// 画刻度
for (var i = 0; i < 60; i++) {
// 循环画60个刻度线
paint.strokeWidth = 5; // 刻度线的宽度
var tickLength = 15;
if (i % 5 == 0) {
paint.strokeWidth = 10;
tickLength = 30; // 如果是5的倍数,则刻度线长度大,否则短
}
var tickX1 = centerX + radius * cos(i * 6 * pi / 180); // 计算刻度线起点的X坐标
var tickY1 = centerY + radius * sin(i * 6 * pi / 180); // 计算刻度线起点的Y坐标
var tickX2 = centerX +
(radius - tickLength) * cos(i * 6 * pi / 180); // 计算刻度线终点的X坐标
var tickY2 = centerY +
(radius - tickLength) * sin(i * 6 * pi / 180); // 计算刻度线终点的Y坐标
canvas.drawLine(
Offset(tickX1, tickY1), Offset(tickX2, tickY2), paint); // 在画布上画刻度线
}
int hour = dateTime.hour;
int minute = dateTime.minute;
int second = dateTime.second;
// 画时针,时针:1小时30°,1分钟0.5°, 乘以 (pi / 180) 把角度转成弧度
paint.strokeWidth = 12;
var a = (hour * 30 + minute * 0.5) - 90;
final hourHandX = centerX + radius * 0.4 * cos(a * pi / 180); // 计算时针的X坐标
final hourHandY = centerY + radius * 0.4 * sin(a * pi / 180); // 计算时针的Y坐标
paint.color = Colors.red; // 设置画笔颜色为红色
canvas.drawLine(center, Offset(hourHandX, hourHandY), paint); // 在画布上画
// 画分针,分针:1分钟6°,1秒钟0.1°
paint.strokeWidth = 8;
a = (minute * 6 + second * 0.1) - 90;
final minuteHandX = centerX + radius * 0.6 * cos(a * pi / 180); // 计算分针的X坐标
final minuteHandY = centerY + radius * 0.6 * sin(a * pi / 180); // 计算分针的Y坐标
paint.color = Colors.green; // 设置画笔颜色为红色
canvas.drawLine(center, Offset(minuteHandX, minuteHandY), paint); // 在画布上画
// 画秒针,秒针,1秒6°
paint.strokeWidth = 5;
a = (second * 6) - 90;
final secondHandX = centerX + radius * 0.8 * cos(a * pi / 180); // 计算秒针的X坐标
final secondHandY = centerY + radius * 0.8 * sin(a * pi / 180); // 计算秒针的Y坐标
paint.color = Colors.blue; // 设置画笔颜色为蓝色
canvas.drawLine(center, Offset(secondHandX, secondHandY), paint); // 在画布上画
paint.color = Colors.black;
paint.style = PaintingStyle.fill;
canvas.drawCircle(center, 8, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
if (dateTime != (oldDelegate as ClockPainter).dateTime) {
return true; // 要重绘
} else {
return false;
}
}
}
效果如下: