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

Flutter fl_chart实时图表:如何动态配置XY轴显示?

问题描述

我在Flutter应用里用fl_chart实现了一个实时刷新的图表:Y轴对应亮度值(double类型,范围0-30),X轴是事件的毫秒级时间戳(int类型)。需要完成两个动态配置:

  • Y轴刻度范围自动设为「数据最小值-1,数据最大值+1」
  • X轴要把时间戳转成HH:mm:ss格式,且当图表展示时长较长(如数小时)时,能自适应时间尺度,固定显示一定数量的刻度

当前实现与问题

图表每秒刷新并添加数据的代码:

void initState() {
  super.initState();
  timer = Timer.periodic(const Duration(seconds: 1), (timer) {
    setState(() {
      currentDateTime = DateTime.now();
      xValue = DateTime.now().millisecondsSinceEpoch as double;
      // widget.SQMTELValue 在其他地方异步更新
      SQMTelPoints.add(FlSpot(xValue, widget.SQMTELValue));
    });
  });
}

我尝试用AxisTitles和bottomTitleWidgets自定义X轴,但未成功,相关配置代码:

titlesData: FlTitlesData(
  show: true,
  // 尝试自定义AxisTitles
  bottomTitles: AxisTitles(sideTitles: bottomTitle),
  topTitles: const AxisTitles(
    sideTitles: SideTitles(showTitles: false)
  )
),
SideTitles get bottomTitle => const SideTitles(
  showTitles: true,
  reservedSize: 32,
  interval: 1,
  getTitlesWidget: bottomTitleWidgets,
);
Widget bottomTitleWidgets(double value, TitleMeta meta) {
  const style = TextStyle(
    fontWeight: FontWeight.bold,
    fontSize: 16,
  );

  // 这里应该是实现逻辑的地方

  return SideTitleWidget(
    axisSide: meta.axisSide,
    space: 10,
    child: text,
    fitInside: SideTitleFitInsideData.disable(),
  );
}

当前图表X轴显示大量混乱数值,Y轴为手动设置的固定刻度,效果如图:
当前图表效果


解决方案

1. 动态调整Y轴刻度范围

通过计算所有数据点的最小/最大Y值,动态设置LineChartData的minY和maxY:

首先添加计算方法:

double getMinY() {
  if (SQMTelPoints.isEmpty) return -1; // 空数据时的默认值
  return SQMTelPoints.map((spot) => spot.y).reduce((a, b) => a < b ? a : b) - 1;
}

double getMaxY() {
  if (SQMTelPoints.isEmpty) return 31; // 空数据时的默认值
  return SQMTelPoints.map((spot) => spot.y).reduce((a, b) => a > b ? a : b) + 1;
}

然后在LineChartData中配置Y轴:

LineChartData(
  // ...其他现有配置
  minY: getMinY(),
  maxY: getMaxY(),
  // 可选:优化Y轴刻度显示
  leftTitles: AxisTitles(
    sideTitles: SideTitles(
      showTitles: true,
      reservedSize: 40,
      getTitlesWidget: (value, meta) {
        return Text(
          value.toStringAsFixed(1),
          style: const TextStyle(fontSize: 12),
        );
      },
    ),
  ),
)

这样每次数据更新时,Y轴范围会自动贴合数据上下限并留出1的缓冲空间。

2. X轴时间戳转格式+自适应刻度

问题核心是你设置了interval:1,导致每个数据点都生成刻度,时间戳数值过大就会挤成一团。修改如下:

第一步:修改X轴刻度配置

去掉固定interval,让fl_chart自动计算合适间隔,或者手动固定刻度数量:

SideTitles get bottomTitle {
  if (SQMTelPoints.isEmpty) return const SideTitles(showTitles: false);
  
  // 方案1:让系统自动计算间隔(推荐)
  return SideTitles(
    showTitles: true,
    reservedSize: 32,
    // 去掉固定interval,由系统根据数据范围自适应
    getTitlesWidget: bottomTitleWidgets,
  );

  // 方案2:固定显示5个刻度,手动计算间隔
  // double minX = SQMTelPoints.first.x;
  // double maxX = SQMTelPoints.last.x;
  // double interval = (maxX - minX) / 4; // 5个刻度对应4个间隔
  // return SideTitles(
  //   showTitles: true,
  //   reservedSize: 32,
  //   interval: interval,
  //   getTitlesWidget: bottomTitleWidgets,
  // );
}

第二步:完善时间戳转格式方法

把毫秒时间戳转成HH:mm:ss格式:

Widget bottomTitleWidgets(double value, TitleMeta meta) {
  const style = TextStyle(
    fontWeight: FontWeight.bold,
    fontSize: 12,
  );

  // 将毫秒时间戳转为DateTime并格式化
  DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(value.toInt());
  String timeStr = "${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}:${dateTime.second.toString().padLeft(2, '0')}";

  return SideTitleWidget(
    axisSide: meta.axisSide,
    space: 8,
    child: Text(timeStr, style: style),
  );
}

额外优化(可选)

如果图表运行时间过长,数据点会过多导致性能下降,可以限制保留的最新数据点数量:

void initState() {
  super.initState();
  timer = Timer.periodic(const Duration(seconds: 1), (timer) {
    setState(() {
      currentDateTime = DateTime.now();
      xValue = DateTime.now().millisecondsSinceEpoch as double;
      SQMTelPoints.add(FlSpot(xValue, widget.SQMTELValue));
      // 只保留最近1000个数据点
      if (SQMTelPoints.length > 1000) {
        SQMTelPoints.removeAt(0);
      }
    });
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 12:53:10