You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
  }
}

问题原因

  1. 角度步长计算错误:
    要绘制12个等分扇形,每个扇形的角度为π/6(对应30°),所以每次的角度步长应等于单个扇形的角度。但当前代码中stepup每次增加0.5弧度(约28.6°),12次累加后总角度为6弧度,而完整圆周是2π≈6.283弧度,少了约0.283弧度,直接导致圆环出现缺口。

  2. 绘制区域可能非正圆:
    如果传入的width和height不一致,绘制的是椭圆而非正圆,椭圆上的弧度对应的视觉角度会有偏差,也可能导致拼接处出现缝隙。

  3. 随机颜色生成冗余:
    每次调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 15:15:27