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

如何在Fl Chart折线图任意位置获取Y坐标并显示LineBarSpot

解决Fl Chart点击任意区域显示对应Y值(保留两位小数)的问题

要实现点击折线图任意区域都显示对应Y值(保留两位小数),核心是准确转换触摸坐标到图表逻辑坐标,并通过插值计算折线对应位置的Y值,而不是依赖默认的已定义spots。以下是修改后的完整方案:

首先在你的State类中添加全局Key:

final _chartKey = GlobalKey<LineChartState>();

然后修改data()方法中的LineTouchData和相关逻辑:

LineChartData data() {
  return LineChartData(
    borderData: FlBorderData(show: false),
    gridData: FlGridData(
      show: true,
      drawVerticalLine: false,
      horizontalInterval: widget.lineInterval,
      getDrawingHorizontalLine: (value) {
        return FlLine(
          color: ColorsTheme.lightGray,
          strokeWidth: 1,
        );
      },
    ),
    titlesData: FlTitlesData(
      show: true,
      rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
      topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
      bottomTitles: AxisTitles(
        sideTitles: SideTitles(
          showTitles: true,
          reservedSize: 25,
          interval: 1,
          getTitlesWidget: titles,
        ),
      ),
      leftTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
    ),
    minX: 0,
    maxX: 4,
    minY: 0,
    maxY: widget.maxY,
    lineTouchData: LineTouchData(
      // 禁用内置触摸处理,改用自定义逻辑
      handleBuiltInTouches: false,
      touchTooltipData: LineTouchTooltipData(
        tooltipBgColor: Colors.white.withOpacity(0.8),
        tooltipBorder: const BorderSide(color: ColorsTheme.darkGray),
        tooltipPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 4),
        tooltipRoundedRadius: 30,
        getTooltipItems: (touchedSpots) {
          return touchedSpots.map((barSpot) {
            // 强制保留两位小数
            final formattedY = barSpot.y.toStringAsFixed(2);
            return LineTooltipItem(
              formattedY,
              const TextStyle(
                color: ColorsTheme.text,
                fontWeight: FontWeight.bold,
              ),
            );
          }).toList();
        },
      ),
      // 自定义触摸回调
      touchCallback: (FlTouchEvent event, LineTouchResponse? response) {
        if (event is FlTapUpEvent && response != null) {
          // 获取图表渲染对象,用于坐标转换
          final renderBox = _chartKey.currentContext?.findRenderObject() as RenderBox?;
          if (renderBox == null) return;

          // 将全局触摸坐标转为图表局部坐标
          final localPos = renderBox.globalToLocal(event.localPosition);
          final chartBounds = response.lineChartState.chartBounds;

          // 转换为图表逻辑坐标(X/Y轴的实际数值)
          final logicX = response.lineChartState.toLogicX(localPos.dx, chartBounds);
          final spots = widget.points;
          if (spots.isEmpty) return;

          // 计算触摸X值对应的折线Y值(插值处理)
          double interpolatedY = 0;
          // 处理X值超出首尾的情况
          if (logicX <= spots.first.x) {
            interpolatedY = spots.first.y;
          } else if (logicX >= spots.last.x) {
            interpolatedY = spots.last.y;
          } else {
            // 找到X值所在的spots区间,进行线性插值
            for (int i = 0; i < spots.length - 1; i++) {
              final spot1 = spots[i];
              final spot2 = spots[i + 1];
              if (logicX >= spot1.x && logicX <= spot2.x) {
                final t = (logicX - spot1.x) / (spot2.x - spot1.x);
                // 线性插值,若需要更精准的曲线插值,可参考FlChart的CatmullRom曲线实现
                interpolatedY = spot1.y + t * (spot2.y - spot1.y);
                break;
              }
            }
          }

          // 创建自定义触摸点,触发tooltip显示
          final customSpot = LineBarSpot(
            barIndex: 0,
            spot: FlSpot(logicX, interpolatedY),
            lineChartBarData: response.lineChartData.lineBarsData[0],
          );
          response.lineChartState.setTouchedSpots([customSpot]);
        }
        // 取消触摸时清除tooltip
        else if (event is FlPanEndEvent || event is FlTapCancelEvent) {
          response?.lineChartState.setTouchedSpots([]);
        }
      },
    ),
    lineBarsData: [
      LineChartBarData(
        spots: widget.points,
        isCurved: true,
        color: ColorsTheme.mainBlue,
        barWidth: 3,
        isStrokeCapRound: true,
        dotData: FlDotData(show: false),
        show: true,
      ),
    ],
  );
}

最后在构建LineChart时绑定全局Key:

LineChart(
  key: _chartKey,
  data: data(),
)

关键说明

  1. 坐标转换:使用FlChart内置的toLogicX/toLogicY方法,避免手动计算偏移量带来的误差,自动适配图表的缩放、padding等布局变化。
  2. 插值计算:通过线性插值处理触摸点在两个spots之间的情况;如果需要更精准的曲线插值,可以参考FlChart源码中的CurveLineDrawer类实现CatmullRom曲线的点计算逻辑。
  3. 自定义触摸点:通过创建LineBarSpot并调用setTouchedSpots,让默认的Tooltip组件显示我们计算出的Y值,无需额外自定义Tooltip组件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:35:13