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

Flutter从Firebase取数据转DateTimeRange时栈溢出问题排查

从Firebase解析数据转换为DateTimeRange列表时栈溢出问题

问题描述

使用预订日历包时,需要从Firebase读取数据并转换为DateTimeRange列表。查询无数据的时间范围时能得到空列表,但查询有数据的范围时出现栈溢出错误,确认数据已被读取但无法解析。

代码展示

final bookings = FirebaseFirestore.instance.collection('bookings');

/// 获取数据引用并通过Firestore的[withConverter]序列化数据
/// 该函数通常放在仓库类中
CollectionReference<SportBooking> getBookingStream({required String placeId}) {
  return bookings.doc(placeId).collection('bookings').withConverter<SportBooking>(
        fromFirestore: (snapshots, _) => SportBooking.fromJson(snapshots.data()!),
        toFirestore: (snapshots, _) => snapshots.toJson(),
      );
}

/// 从Firestore获取数据流(注意:原查询过滤逻辑需适配自身数据结构)
/// 无数据时返回空转换列表,但有数据时触发栈溢出错误,确认数据已读取但无法解析为DateTimeRange列表

Stream<dynamic>? getBookingStreamFirebase(
  {required DateTime end, required DateTime start}) {
     return FirebaseFirestore.instance.collection('bookings').withConverter<SportBooking>(
      fromFirestore:  (snapshots, _) => SportBooking.fromJson(snapshots.data()!),
        toFirestore: (snapshots, _) => snapshots.toJson(),
      ).where('bookingStart', isGreaterThanOrEqualTo: DateTime.now()
                      .where('bookingStart', isLessThanOrEqualTo: DateTime.now().add(const Duration(days: 50))).snapshots();
}

/// 将Firestore返回的数据转换为DateTimeRange列表
List<DateTimeRange> convertStreamResultFirebase(
  {required dynamic streamResult}) {

  /// 需要将快照转换为List<DateTimeRange>
  // final data = snapshot.requireData; **// 这是最关键的一行,但我没理解清楚**

  /// 解析streamResult并转换为[List<DateTimeRange>]
  /// 注意参数为dynamic,需明确结果的可用属性,本例中SportBooking包含bookingStart和bookingEnd属性
    List<DateTimeRange> converted = []
    for (var i = 0; i < data.size; i++) {
      final item = data.docs[i].data();
      converted.add(DateTimeRange(start: (item.bookingStart!), end: (item.bookingEnd!)));
      }
  return converted;
}

Firestore数据结构

Firestore数据存储结构


错误原因及修复方案

1. 查询方法的语法错误(栈溢出核心原因)

getBookingStreamFirebase中where条件嵌套写法错误,把第二个where调用写在了DateTime.now()对象上,导致生成异常的查询逻辑,有数据返回时触发栈溢出。正确写法应为链式调用where:

Stream<QuerySnapshot<SportBooking>> getBookingStreamFirebase({required DateTime end, required DateTime start}) {
  return FirebaseFirestore.instance
      .collection('bookings')
      .withConverter<SportBooking>(
        fromFirestore: (snapshots, _) => SportBooking.fromJson(snapshots.data()!),
        toFirestore: (snapshots, _) => snapshots.toJson(),
      )
      // 链式调用where,而非嵌套在DateTime对象后
      .where('bookingStart', isGreaterThanOrEqualTo: start)
      .where('bookingStart', isLessThanOrEqualTo: end)
      .snapshots();
}

2. 转换方法的变量未定义问题

convertStreamResultFirebase中直接使用未定义的data变量,且参数类型模糊。需明确参数类型并正确提取数据:

List<DateTimeRange> convertStreamResultFirebase({required QuerySnapshot<SportBooking> snapshot}) {
  List<DateTimeRange> converted = [];
  for (var doc in snapshot.docs) {
    final item = doc.data();
    converted.add(DateTimeRange(start: item.bookingStart!, end: item.bookingEnd!));
  }
  return converted;
}

更简洁的写法:

List<DateTimeRange> convertStreamResultFirebase({required QuerySnapshot<SportBooking> snapshot}) {
  return snapshot.docs.map((doc) {
    final booking = doc.data();
    return DateTimeRange(start: booking.bookingStart!, end: booking.bookingEnd!);
  }).toList();
}

3. 类型安全优化

避免使用dynamic类型,明确指定Stream的类型为Stream<QuerySnapshot<SportBooking>>,编译期即可发现错误,避免运行时异常。

4. Timestamp与DateTime的转换验证

从Firestore截图看,bookingStart和bookingEnd为Timestamp类型,需确保SportBooking的fromJson方法正确转换:

class SportBooking {
  final DateTime? bookingStart;
  final DateTime? bookingEnd;

  SportBooking({this.bookingStart, this.bookingEnd});

  factory SportBooking.fromJson(Map<String, dynamic> json) {
    return SportBooking(
      // 正确将Timestamp转换为DateTime
      bookingStart: json['bookingStart'] is Timestamp 
          ? (json['bookingStart'] as Timestamp).toDate() 
          : null,
      bookingEnd: json['bookingEnd'] is Timestamp 
          ? (json['bookingEnd'] as Timestamp).toDate() 
          : null,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'bookingStart': bookingStart != null ? Timestamp.fromDate(bookingStart!) : null,
      'bookingEnd': bookingEnd != null ? Timestamp.fromDate(bookingEnd!) : null,
    };
  }
}

转换逻辑错误会导致访问bookingStart/bookingEnd时触发异常,进而引发栈溢出。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 23:06:09