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

如何完善Flutter中基于Syncfusion的动态分段仪表盘UI

使用syncfusion_flutter_gauges实现指定仪表盘UI的问题解决

待解决问题

  • 指针后的填充颜色效果未实现
  • 仪表盘无法实现均等分段
  • 如何保证仪表盘在全设备上的响应式适配

已实现代码

仪表盘主体代码

return SfRadialGauge(
  title: GaugeTitle(
      text: 'Speedometer',
      textStyle:
          const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)),
  axes: <RadialAxis>[
    RadialAxis(
      minimum: 0,
      maximum: 320,
      startAngle: 180,
      endAngle: 0,
      // showLabels: false,
      maximumLabels: 320,
      onLabelCreated: _handleLabelCreated,
      canRotateLabels: true,
      labelsPosition: ElementsPosition.outside,
      ticksPosition: ElementsPosition.outside,
      showTicks: false,
      radiusFactor: 1.1,
      axisLineStyle: AxisLineStyle(
          thickness: 5,
          dashArray: <double>[(320+100) / 5, 3],
          gradient: SweepGradient(colors: <Color>[
            Color(0xFFE7627D),
            Color(0xFF231557),
            Color(0xFF44107A),
            Color(0xFFFF1361),
            Color(0xFFFFF800),
          ], stops: <double>[
            0,
            100,
            190,
            240,
            320
          ])
      ),
      annotations: <GaugeAnnotation>[
        GaugeAnnotation(
            angle: 180,
            horizontalAlignment: GaugeAlignment.near,
            positionFactor: 0.78,
            verticalAlignment: GaugeAlignment.near,
            widget: Text(
              "0",
              style: TextStyle(
                color: Colors.black,
                fontWeight: FontWeight.bold,
                fontSize: 14,
              ),
            )),
        GaugeAnnotation(
            angle: 0,
            horizontalAlignment: GaugeAlignment.far,
            positionFactor: 0.85,
            verticalAlignment: GaugeAlignment.near,
            widget: Text(
              "320",
              style: TextStyle(
                color: Colors.black,
                fontWeight: FontWeight.bold,
                fontSize: 14,
              ),
            )),
        GaugeAnnotation(
            axisValue: 100,
            positionFactor: 0.5,
            angle: 90,
            widget: Column(children: [
              Center(
                child: RichText(
                    textAlign: TextAlign.center,
                    text: TextSpan(
                      text: '223\n',
                      style: TextStyle(
                        color: Colors.black,
                        fontWeight: FontWeight.bold,
                        fontSize: 30,
                      ),
                      children: [
                        TextSpan(
                          text: 'Points earned this month',
                          style: TextStyle(
                            color: Colors.black,
                            fontWeight: FontWeight.normal,
                            fontSize: 12,
                          ),
                        )
                      ],
                    )),
              )
            ]))
      ],
      pointers: <GaugePointer>[
        MarkerPointer(
            value: 223,
            markerHeight: 15,
            markerWidth: 15,
            enableDragging: true,
            overlayRadius: 12,
            borderColor: Colors.green,
            borderWidth: 2,
            color: Colors.white,
            markerType: MarkerType.circle)
      ],
    )
  ]);

标签处理代码

void _handleLabelCreated(AxisLabelCreatedArgs args) {
  if (args.text == '30') {
    args.text = '£0 - £4';
  } else if (args.text == '100') {
    args.text = '£4 - £8';
  } else if (args.text == '165') {
    args.text = '£8 - £12';
  } else if (args.text == '235') {
    args.text = '£12 - £16';
  } else if (args.text == '290') {
    args.text = '£16 - £20';
  } else {
    args.text = '';
  }
}

效果对比

目标UI效果

仪表盘效果图

当前完成效果

当前完成效果

解决方案

1. 实现指针后的填充颜色效果

通过添加RangePointer实现指针到起点的填充色,将其放在MarkerPointer之前,确保显示在指针下方:

pointers: <GaugePointer>[
  // 指针填充色
  RangePointer(
    value: 223, // 与MarkerPointer的值保持一致
    width: 5, // 与axisLine的thickness一致
    gradient: SweepGradient(
      colors: const [
        Color(0xFFE7627D),
        Color(0xFF231557),
        Color(0xFF44107A),
        Color(0xFFFF1361),
      ],
      stops: const [0, 0.3125, 0.6, 0.75], // 对应分段比例
    ),
    cornerStyle: CornerStyle.bothFlat,
  ),
  MarkerPointer(
    value: 223,
    markerHeight: 15,
    markerWidth: 15,
    enableDragging: true,
    overlayRadius: 12,
    borderColor: Colors.green,
    borderWidth: 2,
    color: Colors.white,
    markerType: MarkerType.circle
  )
],

动态更新时同步修改两个指针的value即可。

2. 实现均等分段

最大值320分为5段,每段64,修改RadialAxis配置:

RadialAxis(
  minimum: 0,
  maximum: 320,
  interval: 64, // 均等分段间隔
  startAngle: 180,
  endAngle: 0,
  showLabels: true,
  maximumLabels: 6, // 0/64/128/192/256/320共6个标签
  onLabelCreated: (args) {
    switch(args.value.toInt()) {
      case 0:
        args.text = '£0 - £4';
        break;
      case 64:
        args.text = '£4 - £8';
        break;
      case 128:
        args.text = '£8 - £12';
        break;
      case 192:
        args.text = '£12 - £16';
        break;
      case 256:
        args.text = '£16 - £20';
        break;
      case 320:
        args.text = '';
        break;
      default:
        args.text = '';
    }
  },
  axisLineStyle: AxisLineStyle(
    thickness: 5,
    dashArray: const [12.8, 3], // 每段虚线长度均等
    gradient: SweepGradient(
      colors: const [
        Color(0xFFE7627D),
        Color(0xFF231557),
        Color(0xFF44107A),
        Color(0xFFFF1361),
        Color(0xFFFFF800),
      ],
      stops: const [0, 0.2, 0.4, 0.6, 1], // 均等渐变节点
    ),
  ),
  // 其他配置保持不变
)

3. 响应式适配方案

结合MediaQuery和LayoutBuilder动态调整尺寸:

LayoutBuilder(
  builder: (context, constraints) {
    final screenWidth = MediaQuery.of(context).size.width;
    final gaugeSize = screenWidth * 0.9; // 占屏幕宽度90%
    final baseFontSize = screenWidth * 0.03;

    return SizedBox(
      width: gaugeSize,
      height: gaugeSize,
      child: SfRadialGauge(
        title: GaugeTitle(
          text: 'Speedometer',
          textStyle: TextStyle(
            fontSize: baseFontSize * 0.5,
            fontWeight: FontWeight.bold
          ),
        ),
        axes: <RadialAxis>[
          RadialAxis(
            radiusFactor: 0.9,
            labelStyle: TextStyle(fontSize: baseFontSize * 0.4),
            annotations: <GaugeAnnotation>[
              GaugeAnnotation(
                widget: Text(
                  "0",
                  style: TextStyle(
                    fontSize: baseFontSize * 0.4,
                    fontWeight: FontWeight.bold
                  ),
                ),
                // 其他标注配置
              ),
            ],
            pointers: <GaugePointer>[
              MarkerPointer(
                markerHeight: screenWidth * 0.04,
                markerWidth: screenWidth * 0.04,
                // 其他指针配置
              )
            ],
            // 其他轴配置
          )
        ],
      ),
    );
  },
)

将所有可缩放元素(尺寸、字体、指针大小)设置为屏幕宽度的比例,确保跨设备自适应。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 21:20:19