如何用Flutter Graphics绘制并旋转圆弧?及实现数值驱动红圆弧动画
Flutter 绘图问题解决方案
1. 绘制圆弧并实现旋转
核心通过CustomPaint配合CustomPainter实现,利用Canvas的旋转API完成圆弧旋转(注意旋转默认绕坐标系原点,需先平移到圆弧中心):
实现步骤
- 自定义
CustomPainter重写paint()方法 - 用
canvas.translate()将坐标系原点移至圆弧中心 - 调用
canvas.rotate()传入旋转弧度(弧度=角度×π/180) - 通过
canvas.drawArc()绘制圆弧,指定矩形范围、起始角度、扫过角度等参数
代码示例
class RotatingArcPainter extends CustomPainter { final double rotationAngle; // 旋转角度(弧度) RotatingArcPainter(this.rotationAngle); @override void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height / 2); final radius = min(size.width, size.height) / 2 - 20; final rect = Rect.fromCircle(center: center, radius: radius); canvas.save(); // 平移到圆弧中心,避免旋转偏离位置 canvas.translate(center.dx, center.dy); canvas.rotate(rotationAngle); canvas.translate(-center.dx, -center.dy); final arcPaint = Paint() ..color = Colors.blue ..style = PaintingStyle.stroke ..strokeWidth = 10 ..strokeCap = StrokeCap.round; // 从12点钟方向开始,绘制半圆 canvas.drawArc(rect, -pi / 2, pi, false, arcPaint); canvas.restore(); } @override bool shouldRepaint(covariant RotatingArcPainter oldDelegate) { return oldDelegate.rotationAngle != rotationAngle; } } // 页面使用示例 class RotatingArcPage extends StatefulWidget { @override State<RotatingArcPage> createState() => _RotatingArcPageState(); } class _RotatingArcPageState extends State<RotatingArcPage> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _rotationAnim; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 2), vsync: this, )..repeat(); _rotationAnim = Tween<double>(begin: 0, end: 2 * pi).animate(_controller); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: CustomPaint( size: const Size(300, 300), painter: RotatingArcPainter(_rotationAnim.value), ), ), ); } }
2. 设计红色进度圆弧并实现数值驱动动画
红色圆弧作为进度指示器,通过数值百分比控制圆弧扫过角度,配合动画实现平滑过渡:
实现思路
- 进度值范围0~1(如60%对应0.6),扫过角度为
2π × 进度值 - 用
AnimationController和Tween实现进度从0到目标值的动画 - 在
CustomPainter中根据当前动画进度绘制红色圆弧,配置描边样式、圆角端点
代码示例
class ProgressArcPainter extends CustomPainter { final double progress; // 0~1的进度值 ProgressArcPainter(this.progress); @override void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height / 2); final radius = min(size.width, size.height) / 2 - 20; final rect = Rect.fromCircle(center: center, radius: radius); // 绘制背景灰色圆弧 final bgPaint = Paint() ..color = Colors.grey.shade300 ..style = PaintingStyle.stroke ..strokeWidth = 12; canvas.drawArc(rect, -pi / 2, 2 * pi, false, bgPaint); // 绘制红色进度圆弧 final progressPaint = Paint() ..color = Colors.red ..style = PaintingStyle.stroke ..strokeWidth = 12 ..strokeCap = StrokeCap.round; // 从12点钟方向开始,按进度绘制圆弧 canvas.drawArc(rect, -pi / 2, 2 * pi * progress, false, progressPaint); } @override bool shouldRepaint(covariant ProgressArcPainter oldDelegate) { return oldDelegate.progress != progress; } } // 页面使用示例 class ProgressArcPage extends StatefulWidget { @override State<ProgressArcPage> createState() => _ProgressArcPageState(); } class _ProgressArcPageState extends State<ProgressArcPage> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _progressAnim; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, ); // 目标进度60% _progressAnim = Tween<double>(begin: 0, end: 0.6).animate(CurvedAnimation( parent: _controller, curve: Curves.easeInOut, )); _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: AnimatedBuilder( animation: _progressAnim, builder: (context, child) { return CustomPaint( size: const Size(300, 300), painter: ProgressArcPainter(_progressAnim.value), ); }, ), ), ); } }
内容的提问来源于stack exchange,提问作者Rafhan Chaudhary
相关产品推荐
相关产品推荐

