Flutter CustomPainter绘制12段环形出现缺口的原因排查
问题描述
在Flutter中使用CustomPainter的drawArc方法绘制12个随机颜色扇形组成完整圆环,先通过以下代码计算响应式尺寸:
if (screenheight - safearea > screenwidth / 2) { blockwidth = screenheight - safearea; blockheight = screenheight - safearea; setState(() {}); } else { blockwidth = screenwidth / 2; blockheight = screenheight - safearea; setState(() {}); }
将计算后的宽高传入InnerRing组件,但最终绘制的环形存在缺口,无法组成完整360°圆环。InnerRing代码如下:
import 'dart:math'; import 'package:flutter/material.dart'; class InnerRing extends StatelessWidget { final double width; final double height; const InnerRing({Key? key, required this.width, required this.height}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: CustomPaint( painter: MaasaRing(width: width, height: height), ), ); } } class MaasaRing extends CustomPainter { final double width; final double height; MaasaRing({required this.width, required this.height}); @override void paint(Canvas canvas, Size size) { double offwidth = width / 4; double offheight = height / 4; double sizewidth = width / 2; double sizeheight = height / 2; Paint arc(int color) { final paint = Paint() ..color = Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0) ..style = PaintingStyle.stroke ..strokeWidth = 18; return paint; } double stepup = 0; for (int i = 0; i < 12; i++) { canvas.drawArc(Offset(offwidth, offheight) & Size(sizewidth, sizeheight), stepup, 1 * pi / 6, false, arc(i)); stepup = stepup + .5; print(stepup); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } }
问题原因
角度步长计算错误:
要绘制12个等分扇形,每个扇形的角度为π/6(对应30°),所以每次的角度步长应等于单个扇形的角度。但当前代码中stepup每次增加0.5弧度(约28.6°),12次累加后总角度为6弧度,而完整圆周是2π≈6.283弧度,少了约0.283弧度,直接导致圆环出现缺口。绘制区域可能非正圆:
如果传入的width和height不一致,绘制的是椭圆而非正圆,椭圆上的弧度对应的视觉角度会有偏差,也可能导致拼接处出现缝隙。随机颜色生成冗余:
每次调用arc(i)都新建Random实例,不仅效率低,还可能出现重复颜色,但这不是缺口的直接原因。
修复代码
修改MaasaRing类的paint方法,调整角度步长、保证绘制正圆并优化颜色生成逻辑:
@override void paint(Canvas canvas, Size size) { // 取宽高中的较小值,确保绘制正圆 final double ringSize = min(width, height); double offwidth = (width - ringSize) / 2; double offheight = (height - ringSize) / 2; double sizewh = ringSize / 2; // 初始化一次Random,避免重复创建实例 final random = Random(); Paint arc() { final paint = Paint() ..color = Color((random.nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0) ..style = PaintingStyle.stroke ..strokeWidth = 18; return paint; } const double segmentAngle = pi / 6; // 单个扇形的角度(30°) double currentAngle = 0; for (int i = 0; i < 12; i++) { canvas.drawArc( Offset(offwidth, offheight) & Size(sizewh, sizewh), currentAngle, segmentAngle, false, arc(), ); currentAngle += segmentAngle; } }
内容的提问来源于stack exchange,提问作者gputhige
相关产品推荐
相关产品推荐

