Flutter中如何在CircularPercentIndicator环形内添加弧形标题文本?
实现堆叠环形进度条内的环绕式标题
我正在开发一个仪表盘项目,使用堆叠的CircularPercentIndicator组件,现在需要给每个环形内部添加贴合弧度的环绕式标题,现有代码如下:
@override Widget build(BuildContext context) { return CircularPercentIndicator( radius: 170.0, animation: true, animationDuration: 1200, lineWidth: 15.0, percent: totAggr, center: CircularPercentIndicator( radius: 150.0, lineWidth: 12.5, animation: true, percent: btc, center: CircularPercentIndicator( radius: 130.0, lineWidth: 12.5, animation: true, percent: eth, center: CircularPercentIndicator( radius: 110, lineWidth: 12.5, percent: alt, center: totAggr > 0.51 ? Image.asset('assets/logos/bull.png') : Image.asset('assets/logos/bear.png'), backgroundColor: Colors.red, progressColor: Colors.green, ), circularStrokeCap: CircularStrokeCap.butt, backgroundColor: Colors.red, progressColor: Colors.green, ), circularStrokeCap: CircularStrokeCap.butt, backgroundColor: Colors.red, progressColor: Colors.green, ), footer: const Text( "BULL/BEAR", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0), ), circularStrokeCap: CircularStrokeCap.butt, backgroundColor: Colors.red, progressColor: Colors.green, ); }
当前效果如图,外圈示例为预期的环绕标题效果:
解决方案
CircularPercentIndicator本身不支持环绕文本,可通过以下两种方式实现需求:
方案一:使用第三方包circular_text
- 在
pubspec.yaml中添加依赖:
dependencies: circular_text: ^2.0.0 # 可替换为最新版本
- 改造堆叠结构,用
Stack将环形进度条与环绕文本组合,确保文本贴合环形内部:
@override Widget build(BuildContext context) { return Stack( alignment: Alignment.center, children: [ // 最外层环形进度条 CircularPercentIndicator( radius: 170.0, animation: true, animationDuration: 1200, lineWidth: 15.0, percent: totAggr, circularStrokeCap: CircularStrokeCap.butt, backgroundColor: Colors.red, progressColor: Colors.green, ), // 最外层环绕标题 CircularText( text: "TOTAL AGGREGATE", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14), // 计算文本半径:环形半径 - 线宽一半 - 文本间距 radius: 170 - 15/2 - 8, startAngle: 90, // 文本从顶部开始 direction: CircularTextDirection.clockwise, // 顺时针环绕 ), // 第二层环形+标题 Stack( alignment: Alignment.center, children: [ CircularPercentIndicator( radius: 150.0, lineWidth: 12.5, animation: true, percent: btc, circularStrokeCap: CircularStrokeCap.butt, backgroundColor: Colors.red, progressColor: Colors.green, ), CircularText( text: "BTC PERCENT", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13), radius: 150 - 12.5/2 - 8, startAngle: 90, direction: CircularTextDirection.clockwise, ), // 第三层环形+标题 Stack( alignment: Alignment.center, children: [ CircularPercentIndicator( radius: 130.0, lineWidth: 12.5, animation: true, percent: eth, circularStrokeCap: CircularStrokeCap.butt, backgroundColor: Colors.red, progressColor: Colors.green, ), CircularText( text: "ETH PERCENT", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12), radius: 130 - 12.5/2 - 8, startAngle: 90, direction: CircularTextDirection.clockwise, ), // 最内层环形+中心图标 CircularPercentIndicator( radius: 110, lineWidth: 12.5, percent: alt, center: totAggr > 0.51 ? Image.asset('assets/logos/bull.png') : Image.asset('assets/logos/bear.png'), backgroundColor: Colors.red, progressColor: Colors.green, ), ], ), ], ), // 底部文本 Positioned( bottom: -30, child: const Text( "BULL/BEAR", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0), ), ), ], ); }
方案二:自定义绘制环绕文本
如果不想依赖第三方包,可通过CustomPaint手动绘制:
- 定义自定义绘制器:
import 'dart:math'; import 'package:flutter/material.dart'; class CircularTextPainter extends CustomPainter { final String text; final double radius; final TextStyle style; final double startAngle; CircularTextPainter({ required this.text, required this.radius, required this.style, this.startAngle = 90, }); @override void paint(Canvas canvas, Size size) { final textSpan = TextSpan(text: text, style: style); final textPainter = TextPainter( text: textSpan, textDirection: TextDirection.ltr, ); textPainter.layout(); final anglePerChar = 2 * pi / text.length; final startRadians = startAngle * pi / 180; for (int i = 0; i < text.length; i++) { final char = text[i]; final charSpan = TextSpan(text: char, style: style); final charPainter = TextPainter( text: charSpan, textDirection: TextDirection.ltr, ); charPainter.layout(); final angle = startRadians + i * anglePerChar; final x = size.width / 2 + radius * cos(angle) - charPainter.width / 2; final y = size.height / 2 + radius * sin(angle) - charPainter.height / 2; canvas.save(); canvas.translate(x + charPainter.width / 2, y + charPainter.height / 2); canvas.rotate(angle + pi / 2); // 旋转字符使其垂直于半径 canvas.translate(-charPainter.width / 2, -charPainter.height / 2); charPainter.paint(canvas, Offset.zero); canvas.restore(); } } @override bool shouldRepaint(CircularTextPainter oldDelegate) { return text != oldDelegate.text || radius != oldDelegate.radius || style != oldDelegate.style || startAngle != oldDelegate.startAngle; } }
- 使用自定义绘制器:
CustomPaint( size: Size(324, 324), // 对应环形直径:2*(170-15/2-8) painter: CircularTextPainter( text: "TOTAL AGGREGATE", radius: 170 - 15/2 - 8, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14), ), )
注意事项
- 调整文本字号和间距,避免文本超出环形范围
- 可修改
startAngle和环绕方向,实现不同的文本起始位置和走向 - 堆叠层级需注意,确保文本显示在环形上方(或根据需求调整)
内容的提问来源于stack exchange,提问作者Adam Marc
相关产品推荐
相关产品推荐

