Flutter fl_chart绘制日期折线图 日期过多、配置minX/maxX卡顿问题
问题根因
两个核心逻辑错误直接触发你遇到的所有异常:
- 折线坐标与轴范围完全错配:生成折线点位时,你把X值替换成了列表索引(0、1、2……这类个位数),但计算
minX/maxX时用的是日期转成的13位毫秒时间戳,两者量级差超过10^10。fl_chart会尝试在跨度达数十亿的数值区间内按默认步长渲染刻度、网格线,计算量直接爆掉,必然卡死。 - 刻度配置逻辑错误:X轴刻度间隔设为首尾日期的总时间差,同时没有做无效刻度过滤,组件会遍历区间内所有毫秒值尝试生成标签,直接出现大量冗余日期。
- 附加卡顿点:网格线横纵间隔都设为1,配合错误的X轴范围,会尝试绘制数百万条透明网格线,进一步拖慢性能。
修复方案
按以下步骤替换对应代码块即可:
- 修正折线点位,不要用索引替换原始时间戳X值
- 重新配置网格线、X轴刻度的间隔值,匹配日期维度的实际步长
- 增加无效刻度过滤逻辑,只渲染实际存在数据的日期标签
- 替换字符串切割的日期格式化逻辑,避免时区偏移导致的日期显示错误
核心代码修改
1. 修正折线点位生成
找到lineBarsData配置块,把原来错误的spots生成逻辑替换为直接使用原始点位:
lineBarsData: [ LineChartBarData( // 直接使用预处理好的flSpots,禁止用列表索引替换X值 spots: flSpots, isCurved: false, color: primaryColorGreen, barWidth: 2, isStrokeCapRound: true, dotData: FlDotData(show: true), belowBarData: BarAreaData( show: true, gradient: LinearGradient( colors: gradientColors.map((color) => color.withOpacity(0.3)).toList(), begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ), ]
2. 修正网格线间隔
找到gridData配置块,替换间隔参数为合理值:
gridData: FlGridData( show: true, drawVerticalLine: true, // Y轴间隔匹配你的销售额步长,示例为每1000单位画一条横线 horizontalInterval: 1000, // X轴间隔匹配日期步长,示例为每1天对应毫秒数,可按需改成7天/30天 verticalInterval: 24 * 60 * 60 * 1000, getDrawingHorizontalLine: (value) => FlLine(color: borderColor, strokeWidth: 1), getDrawingVerticalLine: (value) => FlLine(color: Colors.transparent, strokeWidth: 1), )
3. 增加X轴刻度过滤
在mainData方法开头新增有效X值集合,用来过滤无效刻度:
LineChartData mainData(List<FlSpot>? flSpots) { final List<double> xList = []; final List<double> yList = []; // 存储所有真实存在数据的X轴毫秒值 final Set<double> validXSet = {}; flSpots?.forEach((e) { xList.add(e.x); yList.add(e.y); validXSet.add(e.x); }); final minX = xList.reduce(min); final maxX = xList.reduce(max); final minY = yList.reduce(min); final maxY = ((yList.reduce(max)) + 1000).toInt().toDouble(); // ...... 其他原有配置保留,修改bottomTitles部分
替换bottomTitles配置:
bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 30, interval: 24 * 60 * 60 * 1000, // 过滤无效刻度,只渲染真实有数据的日期 getTitlesWidget: (value, meta) { if (!validXSet.contains(value)) return const SizedBox.shrink(); return bottomTitleWidgets(value, meta); }, ) )
4. 优化日期格式化
提前初始化日期格式化器(建议放在类成员位置,不要每次build重建),替换原来的字符串切割逻辑,避免时区问题:
// 类成员位置初始化,需要引入intl包 final _dateFormatter = DateFormat('yyyy-MM-dd'); Widget bottomTitleWidgets(double value, TitleMeta meta) { const style = TextStyle( height: 1, color: placeholderColor, fontWeight: weight500, fontSize: fontsize12, ); final date = DateTime.fromMillisecondsSinceEpoch(value.toInt()); return SideTitleWidget( space: 12, axisSide: meta.axisSide, child: Text(_dateFormatter.format(date), style: style), ); }
可选优化
- 数据量大于20条时,不需要给每个点都显示X轴标签,可固定只渲染首、中、尾3-5个标签,减少渲染压力
- 计算
minX/maxX时可各增减1天的毫秒值作为padding,避免首尾数据点贴在图表边缘 - FlSpot列表、有效X值集合建议在拿到业务数据时就提前转换完成,不要放在build方法里遍历计算
内容的提问来源于stack exchange,提问作者AndroidAndMe
相关产品推荐
相关产品推荐

