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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:20:59