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

Flutter中如何用Custom Painter绘制非堆叠饼图并添加百分比标签

解决Flutter CustomPainter饼图弧堆叠及百分比显示问题

问题分析

原代码中所有弧的起始角度固定为 -math.pi/2,导致所有弧从同一位置开始绘制,出现堆叠覆盖的情况;同时缺少文本绘制逻辑,无法显示各部分百分比。

修复方案

  1. 累计起始角度:维护变量记录当前弧的起始角度,每次绘制完一个弧后,将起始角度累加当前弧的角度,实现弧的依次衔接。
  2. 绘制百分比文本:计算每个弧的中间角度,结合饼图中心和半径算出文本位置,使用TextPainter绘制百分比文本。

修改后的完整代码

import 'dart:math' as math;

import 'package:flutter/material.dart';

class PieChart extends CustomPainter {
  final List<int> percent = [50, 20, 30];

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()..style = PaintingStyle.fill;
    final double radius = math.min(size.width / 2, size.height / 2);
    final Offset center = Offset(size.width / 2, size.height / 2);
    
    // 累计起始角度,初始从顶部(-π/2)开始
    double startAngle = -math.pi / 2;

    for (int i = 0; i < percent.length; i++) {
      final double arcAngle = 2 * math.pi * (percent[i] / 100);
      // 生成随机颜色
      paint.color = Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
      
      // 绘制衔接的弧
      canvas.drawArc(
        Rect.fromCircle(center: center, radius: radius),
        startAngle,
        arcAngle,
        true,
        paint,
      );

      // 绘制百分比文本
      _drawPercentageText(canvas, center, radius, startAngle, arcAngle, percent[i]);

      // 更新起始角度,为下一个弧做准备
      startAngle += arcAngle;
    }
  }

  void _drawPercentageText(Canvas canvas, Offset center, double radius, double startAngle, double arcAngle, int percentage) {
    // 计算弧的中间角度
    final double middleAngle = startAngle + arcAngle / 2;
    // 文本向内偏移,避免超出饼图
    final double textRadius = radius * 0.7;
    final Offset textOffset = Offset(
      center.dx + math.cos(middleAngle) * textRadius,
      center.dy + math.sin(middleAngle) * textRadius,
    );

    final TextPainter textPainter = TextPainter(
      text: TextSpan(
        text: '$percentage%',
        style: const TextStyle(
          color: Colors.white,
          fontSize: 16,
          fontWeight: FontWeight.bold,
        ),
      ),
      textDirection: TextDirection.ltr,
    );

    textPainter.layout();
    // 将文本居中绘制在目标位置
    textPainter.paint(
      canvas,
      Offset(
        textOffset.dx - textPainter.width / 2,
        textOffset.dy - textPainter.height / 2,
      ),
    );
  }

  @override
  bool shouldRepaint(PieChart oldDelegate) => false;

  @override
  bool shouldRebuildSemantics(PieChart oldDelegate) => false;
}

关键代码说明

  • 弧衔接逻辑:startAngle变量初始指向饼图顶部,每次绘制完弧后累加当前弧的角度,确保下一个弧从当前弧的结束位置开始绘制。
  • 文本绘制逻辑:通过三角函数计算弧的中间位置坐标,使用TextPainter实现文本居中显示,同时设置向内偏移的半径,让文本显示在饼图内部。

内容的提问来源于stack exchange,提问作者Naduel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 22:48:17