Flutter用Firestore数据绘LineChart遇FormatException错误求解决方案
问题解决:Fl_chart结合Firestore绘制折线图报错修正
问题背景
需求:使用fl_chart框架结合Firestore数据绘制折线图,数据包含double类型的
Amount字段和字符串类型的Date字段(由DateTime选择生成),要求底部坐标轴显示日期、左侧坐标轴显示金额。
报错:FormatException: Invalid double 10-Feb-2024,无法生成折线。
当前代码:FutureBuilder( future: FirebaseFirestore.instance .collectionGroup(widget.dailyTransactionsCollection) .get(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } else if (snapshot.hasError) { return Text( widget.errorString, style: const TextStyle(color: Colors.white), ); } if (snapshot.hasData) { List<FlSpot> dataPoints = []; for (var doc in snapshot.data!.docs) { double amount = doc.get('Amount'); String date = doc.get('Date'); FlSpot dataPoint = FlSpot(double.parse(date), amount); dataPoints.add(dataPoint); } LineChartData( lineBarsData: [ LineChartBarData( spots: dataPoints, isCurved: true, color: Colors.blue, barWidth: 2, dotData: const FlDotData(show: false), ), ], titlesData: const FlTitlesData( bottomTitles: AxisTitles( sideTitles: SideTitles(reservedSize: 33, showTitles: true)), leftTitles: AxisTitles(sideTitles: SideTitles(reservedSize: 44)), ), gridData: const FlGridData(show: true), ); } return Center( child: Text(widget.noDataYetString), ); }, )
错误原因
FlSpot的x轴参数要求是数值类型,直接将日期字符串10-Feb-2024通过double.parse()转换会触发格式异常,因为该字符串无法解析为有效的double值。同时原代码未返回LineChart组件,即使数据处理正确也无法显示图表。
修正后的代码
import 'package:intl/intl.dart'; // 引入intl包处理日期格式化 import 'package:flutter/foundation.dart'; // 用于debugPrint FutureBuilder( future: FirebaseFirestore.instance .collectionGroup(widget.dailyTransactionsCollection) .get(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } else if (snapshot.hasError) { return Text( widget.errorString, style: const TextStyle(color: Colors.white), ); } if (snapshot.hasData) { List<FlSpot> dataPoints = []; // 定义日期格式化器,匹配Firestore中的日期字符串格式 final dateFormat = DateFormat('dd-MMM-yyyy'); for (var doc in snapshot.data!.docs) { try { double amount = doc.get('Amount'); String dateStr = doc.get('Date'); // 将日期字符串解析为DateTime,再转换为毫秒时间戳作为x轴数值 DateTime date = dateFormat.parse(dateStr); double xValue = date.millisecondsSinceEpoch.toDouble(); dataPoints.add(FlSpot(xValue, amount)); } catch (e) { // 处理日期解析失败或数据异常的情况 debugPrint('数据处理异常: $e'); continue; } } // 确保数据点不为空再渲染图表 if (dataPoints.isEmpty) { return Center(child: Text(widget.noDataYetString)); } // 返回LineChart组件,原代码遗漏了这一步 return LineChart( LineChartData( lineBarsData: [ LineChartBarData( spots: dataPoints, isCurved: true, color: Colors.blue, barWidth: 2, dotData: const FlDotData(show: true), // 可选:显示数据点 ), ], titlesData: FlTitlesData( bottomTitles: AxisTitles( sideTitles: SideTitles( reservedSize: 33, showTitles: true, // 自定义底部坐标轴标题,将时间戳转回日期字符串 getTitlesWidget: (value, meta) { DateTime date = DateTime.fromMillisecondsSinceEpoch(value.toInt()); return Text( dateFormat.format(date), style: const TextStyle(fontSize: 10), ); }, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( reservedSize: 44, showTitles: true, // 可选:自定义左侧金额显示格式 getTitlesWidget: (value, meta) { return Text('\$${value.toStringAsFixed(2)}'); }, ), ), topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), ), gridData: const FlGridData(show: true), borderData: FlBorderData(show: true), ), ); } return Center( child: Text(widget.noDataYetString), ); }, )
关键修改说明
- 日期转数值:使用
DateFormat解析日期字符串为DateTime,再通过millisecondsSinceEpoch转换为double类型的时间戳,作为FlSpot的x轴值,解决格式转换异常问题。 - 自定义坐标轴标题:在
bottomTitles的getTitlesWidget中,将x轴的时间戳数值转回DateTime并格式化为日期字符串,实现底部显示日期的需求;左侧坐标轴可自定义金额显示格式(如添加货币符号)。 - 组件返回修正:原代码仅创建了
LineChartData但未返回LineChart组件,修正后添加return LineChart(...)确保图表渲染。 - 异常处理:添加try-catch块处理日期解析失败或数据异常的情况,避免单个错误数据导致整个图表崩溃。
- 依赖补充:需要在
pubspec.yaml中添加intl依赖,用于日期格式化:dependencies: intl: ^0.18.1
内容的提问来源于stack exchange,提问作者EBENEZER OCANSEY
相关产品推荐
相关产品推荐

