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
相关产品推荐
相关产品推荐

