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

从Firebase获取数据至折线图对象列表失败的问题排查

问题:Firebase Realtime Database数据无法加载到折线图列表

我尝试从Firebase Realtime Database获取数据并映射到alldataSensor对象列表,用于SfCartesianChart折线图展示,但多次尝试后仍无法成功获取数据。

数据库结构说明

data节点下包含多个子节点,每个子节点包含以下字段:

  • Ts:时间戳(毫秒)
  • EC:传感器EC值
  • PH:传感器PH值
  • RTD:传感器RTD值
  • humidity:湿度值
  • temperature:温度值

现有代码

数据模型与Firebase数据获取代码

class alldataSensor {
  DateTime timeStamp;
  double sensorEc;
  double sensorPh;
  double sensorRtd;
  double humidity;
  double temperature;

  alldataSensor(
      {required this.timeStamp,
      required this.temperature,
      required this.humidity,
      required this.sensorRtd,
      required this.sensorPh,
      required this.sensorEc});


  factory alldataSensor.fromMap(Map<dynamic, dynamic> map) {
    return alldataSensor(
      timeStamp: DateTime.fromMillisecondsSinceEpoch(map['Ts']),
      sensorEc: map['EC'] as double,
      sensorPh: map['PH'] as double,
      sensorRtd: map['RTD'] as double,
      humidity: map['humidity'] as double ,
      temperature: map['temperature'] as double,
    );
  }
}


Future<List<alldataSensor>> getallDataSensor() async {
  DatabaseReference allDataSensor = FirebaseDatabase.instance.ref().child('data');
  print(allDataSensor); // 调试用,查看是否返回数据

  List<alldataSensor> needs = [];
  allDataSensor.onChildAdded.listen((event) {
    DataSnapshot dataSnapshot =event.snapshot;
    final Map<DateTime, dynamic> values = dataSnapshot.value as Map<DateTime, dynamic>;
    values.forEach((key, value) {
      needs.add(value);
    });
  });
  return needs;
}

折线图调用列表代码

List<alldataSensor> _list = [];

  @override
  void initState() {
    super.initState();
    _setupNeeds();
  }

  _setupNeeds() async {
    List<alldataSensor> needs = await getallDataSensor();
    setState(() {
      _list = needs;
    });
  }
  Widget build(BuildContext context) {
    return SfCartesianChart(
      zoomPanBehavior: ZoomPanBehavior(
        enablePanning: true,
        enablePinching: true,
        enableDoubleTapZooming: true,
        enableMouseWheelZooming: true,
        maximumZoomLevel: 0.5,
      ),
      plotAreaBorderWidth: 0,
      primaryXAxis: DateTimeAxis(
        intervalType: DateTimeIntervalType.days,
        dateFormat: DateFormat.yMMMd(),
        interval: 1,
        majorGridLines: const MajorGridLines(width: 0),
      ),
      primaryYAxis: NumericAxis(
        axisLine: const AxisLine(width: 0),
        majorTickLines: const MajorTickLines(width: 0),
      ),
      legend: const Legend(
        isVisible: true,
        position: LegendPosition.bottom,
      ),
      series: <LineSeries<dynamic, DateTime>>[
        LineSeries<alldataSensor, DateTime>(
          name: 'EC',
          legendIconType: LegendIconType.circle,
          dataSource: _list,
          xValueMapper: (alldataSensor data, _) => data.timeStamp,
          yValueMapper: (alldataSensor data, _) => data.sensorEc,
          onRendererCreated: (ChartSeriesController controller) {
            seriesController = controller;
          },
        ),
        LineSeries<alldataSensor, DateTime>(
          name: 'pH',
          legendIconType: LegendIconType.circle,
          dataSource: _list,
          xValueMapper: (alldataSensor data, _) => data.timeStamp,
          yValueMapper: (alldataSensor data, _) => data.sensorPh,
          onRendererCreated: (ChartSeriesController controller) {
            seriesController = controller;
          },
        ),
      ],
      loadMoreIndicatorBuilder:
          (BuildContext context, ChartSwipeDirection direction) {
        if (direction == ChartSwipeDirection.end) {
          getallDataSensor();
          return SizedBox.fromSize(size: Size.zero); // 替换为此代码
        } else {
          return SizedBox.fromSize(size: Size.zero);
        }
      },
    );
}

问题分析与修复方案

1. Firebase数据获取逻辑错误

getallDataSensor函数中使用onChildAdded监听流,但函数在监听触发前就返回了空列表。应该使用get()方法一次性获取所有数据快照,确保数据加载完成后再返回列表。

2. 数据解析错误

Firebase返回的子节点键是字符串类型,且每个子节点的value是包含字段的Map,直接将dataSnapshot.value转为Map<DateTime, dynamic>会报错,需要将每个子节点的value通过fromMap工厂方法转换为alldataSensor对象。

3. 类型转换异常

Firebase返回的数字可能是int类型,直接强制转为double会抛出异常,需要用(num).toDouble()统一处理。

修复后的代码

数据模型的fromMap方法

factory alldataSensor.fromMap(Map<dynamic, dynamic> map) {
  return alldataSensor(
    timeStamp: DateTime.fromMillisecondsSinceEpoch(map['Ts']),
    sensorEc: (map['EC'] as num).toDouble(),
    sensorPh: (map['PH'] as num).toDouble(),
    sensorRtd: (map['RTD'] as num).toDouble(),
    humidity: (map['humidity'] as num).toDouble(),
    temperature: (map['temperature'] as num).toDouble(),
  );
}

Firebase数据获取函数

Future<List<alldataSensor>> getallDataSensor() async {
  DatabaseReference allDataSensor = FirebaseDatabase.instance.ref().child('data');
  DataSnapshot snapshot = await allDataSensor.get();
  
  List<alldataSensor> needs = [];
  if (snapshot.exists) {
    Map<String, dynamic> values = snapshot.value as Map<String, dynamic>;
    values.forEach((key, value) {
      needs.add(alldataSensor.fromMap(value));
    });
  }
  return needs;
}

数据加载函数(增加错误处理)

_setupNeeds() async {
  try {
    List<alldataSensor> needs = await getallDataSensor();
    setState(() {
      _list = needs;
    });
  } catch (e) {
    print('获取数据失败: $e');
  }
}

折线图加载更多逻辑(更新状态)

loadMoreIndicatorBuilder:
    (BuildContext context, ChartSwipeDirection direction) {
  if (direction == ChartSwipeDirection.end) {
    _setupNeeds(); // 调用已有的加载方法更新数据
    return SizedBox.fromSize(size: Size.zero);
  } else {
    return SizedBox.fromSize(size: Size.zero);
  }
},

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 16:15:57