Flutter中如何将文本居中显示在对应弧形区域内?
问题:Flutter中实现弧形文本与对应弧形居中对齐
我正在Flutter应用中尝试将文本居中显示在弧形区域内,当前实现包含两部分:
- 通过CustomPaint在画布上绘制彩色弧形,弧形的位置和长度是动态的(示例使用静态列表,实际从sqflite数据库获取);
- 通过flutter_arc_text组件在弧形上方叠加文本,文本内容随用户选择动态变化。
目前文本无法与对应弧形居中对齐,请教如何实现文本在每个弧形内居中?
绘制弧形的原代码
import 'dart:math'; import 'package:flutter/material.dart'; class OuterRing extends StatelessWidget { final double width; final double height; const OuterRing( {Key? key, required this.width, required this.height, }) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: Stack( children: [ CustomPaint( painter: Ring(width: width, height: height), ) ], ), ); } } class Ring extends CustomPainter { final double width; final double height; Ring( {required this.width, required this.height}); @override void paint(Canvas canvas, Size size) { double sizedim = width / 1.5; double offsetdim = sizedim / 2; double strkwidth = 18; List<Color> colors = [ const Color.fromRGBO(255, 179, 186, 1), const Color.fromRGBO(255, 223, 186, 1), const Color.fromRGBO(255, 255, 186, 1), ]; List<double> start = [ -66.96, -95.085, -122.2725, -150.3975, -177.585, -233.835, -261.0225, -289.1475, -316.335, -344.46, -11.6475, -39.7725 ]; List<double> sweep = [ -28.125, -27.1875, -28.125, -27.1875, -56.25, -27.1875, -28.125, -27.1875, -28.125, -27.1875, -28.125, -27.1875 ]; Paint arc(int color) { final paint = Paint() ..color = colors[color] ..style = PaintingStyle.stroke ..strokeWidth = strkwidth; return paint; } double rad = 0.0174533; int j = 0; for (int i = 0; i < 12; i++) { if (j == 3 || j == 6 || j == 12) { j = 0; } canvas.drawArc( Offset(strkwidth / 2 + width / 2 - offsetdim, strkwidth / 2 + width / 2 - offsetdim) & Size(sizedim - strkwidth, sizedim - strkwidth), start[i] * rad, sweep[i] * rad, false, arc(j)); j++; } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } }
添加弧形文本的原代码
import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_arc_text/flutter_arc_text.dart'; class OuterRingText extends StatelessWidget { final double width; final double height; const OuterRingText( {Key? key, required this.width, required this.height}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: Stack( children: [for (var i = 0; i < 12; i++) arctext(i)], )); } Widget arctext(count) { List<String> names = [ 'First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth', 'Seventh', 'Eighth', 'Nineth', 'Tenth', 'Eleventh', 'Twelfth' ]; List<double> start = [ -66.96, -95.085, -122.2725, -150.3975, -177.585, -233.835, -261.0225, -289.1475, -316.335, -344.46, -11.6475, -39.7725 ]; return Center( child: ArcText( radius: 106, text: names[count], textStyle: const TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.black), startAngle: (start[count] - start[0]) * 0.0174533, startAngleAlignment: StartAngleAlignment.start, placement: Placement.outside, direction: Direction.clockwise), ); } }
解决方案
要实现文本与对应弧形居中对齐,核心是让文本的弧形中心与绘制的弧形中心重合,通过以下调整即可解决:
关键修改逻辑
- 共享数据源:将弧形的
start和sweep角度列表提取为公共常量,避免重复定义导致数据不一致; - 计算文本中心角度:基于弧形的起始角度和扫过角度,计算出弧形的中心角度,以此作为文本的起始基准;
- 对齐方式切换:将
startAngleAlignment设置为center,让文本以计算出的中心角度向两侧展开; - 动态匹配半径:根据绘制弧形的参数动态计算文本半径,避免硬编码导致的偏移。
修改后的OuterRingText代码
import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_arc_text/flutter_arc_text.dart'; // 和绘制弧形的数据源保持一致 const List<double> arcStarts = [ -66.96, -95.085, -122.2725, -150.3975, -177.585, -233.835, -261.0225, -289.1475, -316.335, -344.46, -11.6475, -39.7725 ]; const List<double> arcSweeps = [ -28.125, -27.1875, -28.125, -27.1875, -56.25, -27.1875, -28.125, -27.1875, -28.125, -27.1875, -28.125, -27.1875 ]; class OuterRingText extends StatelessWidget { final double width; final double height; static const double strokeWidth = 18; const OuterRingText({ Key? key, required this.width, required this.height, }) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: Stack( children: [for (var i = 0; i < 12; i++) arcText(i)], ), ); } Widget arcText(int count) { List<String> names = [ 'First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth', 'Seventh', 'Eighth', 'Nineth', 'Tenth', 'Eleventh', 'Twelfth' ]; final radianFactor = pi / 180; // 弧形的起始弧度 final arcStartRadian = arcStarts[count] * radianFactor; // 弧形的扫过弧度 final arcSweepRadian = arcSweeps[count] * radianFactor; // 文本起始角度:弧形中心位置 final textCenterAngle = arcStartRadian + (arcSweepRadian / 2); // 动态计算文本半径:弧形外半径 final textRadius = (width / 1.5) / 2 + strokeWidth / 2; return Center( child: ArcText( radius: textRadius, text: names[count], textStyle: const TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.black, ), startAngle: textCenterAngle, // 以中心角度对齐文本 startAngleAlignment: StartAngleAlignment.center, placement: Placement.outside, direction: Direction.clockwise, ), ); } }
补充说明
- 如果从sqflite动态获取弧形数据,只需将
arcStarts和arcSweeps替换为数据库返回的列表即可,对齐逻辑无需修改; - 若文本长度差异较大,可根据文本长度微调
startAngle或调整字体大小,确保文本完全覆盖对应弧形区域。
内容的提问来源于stack exchange,提问作者gputhige
相关产品推荐
相关产品推荐

