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

Flutter实现数字环绕分段环形进度(径向仪表盘)的免费替代方案咨询

替代方案与实现思路

一、使用开源Flutter包

这些包均采用MIT/Apache协议,商业场景可免费使用:

1. flutter_circular_chart

轻量易配置,支持分段环形/饼图样式,能快速实现基础的分段仪表盘效果。

  • 核心用法:
    CircularChart(
      charts: [
        CircularStackEntry(
          [
            CircularSegmentEntry(30, Colors.red, rankKey: 'red'),
            CircularSegmentEntry(40, Colors.blue, rankKey: 'blue'),
            CircularSegmentEntry(30, Colors.green, rankKey: 'green'),
          ],
          rankKey: 'progress',
        ),
      ],
      chartType: CircularChartType.Radial,
      edgeStyle: SegmentEdgeStyle.round,
      percentageValues: true,
      centerText: '70',
    )
    

2. 自定义Flutter原生CircularProgressIndicator

利用原生组件的strokeCap、color属性结合Stack,可模拟分段效果,无需额外依赖:

  • 示例代码:
    Stack(
      alignment: Alignment.center,
      children: [
        // 背景环
        CircularProgressIndicator(
          value: 1,
          strokeWidth: 12,
          backgroundColor: Colors.grey.shade300,
          valueColor: AlwaysStoppedAnimation(Colors.transparent),
        ),
        // 分段前景环1
        CircularProgressIndicator(
          value: 0.3,
          strokeWidth: 12,
          strokeCap: StrokeCap.round,
          valueColor: AlwaysStoppedAnimation(Colors.red),
        ),
        // 分段前景环2(偏移角度)
        Transform.rotate(
          angle: 2 * pi * 0.3,
          child: CircularProgressIndicator(
            value: 0.4,
            strokeWidth: 12,
            strokeCap: StrokeCap.round,
            valueColor: AlwaysStoppedAnimation(Colors.blue),
          ),
        ),
        // 中间数字
        const Text('70', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
      ],
    )
    

3. flutter_radial_gauge

专为径向仪表盘设计的开源包,支持多分段、刻度、指针等功能,功能接近商业组件但完全免费,可自定义分段颜色、范围、中心文本等细节。

二、自定义绘制(CustomPainter)

如果需要高度定制效果,直接用CustomPainter手动绘制是最灵活的方式,核心步骤为:绘制背景环形 → 按数值比例绘制分段前景环 → 在中心绘制显示数字的文本。

完整示例代码:

class SegmentedRadialGauge extends StatelessWidget {
  final List<Map<String, dynamic>> segments; // 格式: [{value: 30, color: Colors.red}, ...]
  final double totalValue;
  final String centerText;

  const SegmentedRadialGauge({
    super.key,
    required this.segments,
    required this.totalValue,
    required this.centerText,
  });

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: const Size(200, 200),
      painter: _SegmentedGaugePainter(segments, totalValue, centerText),
    );
  }
}

class _SegmentedGaugePainter extends CustomPainter {
  final List<Map<String, dynamic>> segments;
  final double totalValue;
  final String centerText;
  final double strokeWidth = 12;

  _SegmentedGaugePainter(this.segments, this.totalValue, this.centerText);

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (size.width - strokeWidth) / 2;

    // 绘制背景环
    final backgroundPaint = Paint()
      ..color = Colors.grey.shade300
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;
    canvas.drawCircle(center, radius, backgroundPaint);

    // 绘制分段前景环
    double currentAngle = -pi / 2; // 从顶部开始
    for (final segment in segments) {
      final value = segment['value'] as double;
      final color = segment['color'] as Color;
      final sweepAngle = (value / totalValue) * 2 * pi;

      final segmentPaint = Paint()
        ..color = color
        ..style = PaintingStyle.stroke
        ..strokeWidth = strokeWidth
        ..strokeCap = StrokeCap.round;

      canvas.drawArc(
        Rect.fromCircle(center: center, radius: radius),
        currentAngle,
        sweepAngle,
        false,
        segmentPaint,
      );

      currentAngle += sweepAngle;
    }

    // 绘制中心文本
    final textPainter = TextPainter(
      text: TextSpan(
        text: centerText,
        style: const TextStyle(
          fontSize: 24,
          fontWeight: FontWeight.bold,
          color: Colors.black,
        ),
      ),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    textPainter.paint(
      canvas,
      Offset(
        center.dx - textPainter.width / 2,
        center.dy - textPainter.height / 2,
      ),
    );
  }

  @override
  bool shouldRepaint(covariant _SegmentedGaugePainter oldDelegate) {
    return oldDelegate.segments != segments ||
        oldDelegate.totalValue != totalValue ||
        oldDelegate.centerText != centerText;
  }
}

// 使用方式
// SegmentedRadialGauge(
//   segments: [
//     {'value': 30, 'color': Colors.red},
//     {'value': 40, 'color': Colors.blue},
//     {'value': 30, 'color': Colors.green},
//   ],
//   totalValue: 100,
//   centerText: '70',
// )

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:05:17