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

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),
    );
  }
}

解决方案

要实现文本与对应弧形居中对齐,核心是让文本的弧形中心与绘制的弧形中心重合,通过以下调整即可解决:

关键修改逻辑

  1. 共享数据源:将弧形的start和sweep角度列表提取为公共常量,避免重复定义导致数据不一致;
  2. 计算文本中心角度:基于弧形的起始角度和扫过角度,计算出弧形的中心角度,以此作为文本的起始基准;
  3. 对齐方式切换:将startAngleAlignment设置为center,让文本以计算出的中心角度向两侧展开;
  4. 动态匹配半径:根据绘制弧形的参数动态计算文本半径,避免硬编码导致的偏移。

修改后的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 00:00:05