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

Flutter日历无法加载Firestore的List<Event>,类型转换错误求助

问题排查与解决方案

核心问题梳理

  • 类型转换错误:_data()方法直接将异步的Future<List<Event>>强制转为List<Event>,这是触发_TypeError的根本原因。
  • 控制器逻辑缺陷:GetEventsController()中用listen监听Stream,但listen是异步执行的,方法会直接返回空列表,无法正确获取数据;同时错误地从map["id"]读取Firestore文档ID(正确应为doc.id)。
  • 模型字段不匹配:Event.fromMap中读取大写的map["TYPE"],但模型toMap存储的是小写"type",会导致字段读取失败或类型错误。
  • 数据库层类型模糊:GetEvents返回无类型约束的Future,应明确返回Stream<QuerySnapshot>或Future<QuerySnapshot>以避免歧义。

分步修复代码

1. 修正models.dart的fromMap方法

class Event {
  final String Id;
  final String user;
  final String title;
  final String description;
  final String? type;
  final DateTime from;
  final DateTime to;
  final Color backgroundColor;

  Event({
    required this.Id,
    required this.user,
    required this.title,
    required this.description,
    this.type,
    required this.from,
    required this.to,
    this.backgroundColor = pinkColor,
  });

  Map<String, dynamic> toMap() { // 修正方法名首字母小写(符合Dart规范)
    return {
      "Id": Id,
      "user": user,
      "title": title,
      "description": description,
      "type": type,
      "from": from,
      "to": to,
    };
  }

  factory Event.fromMap(Map<String, dynamic> map, String documentId) {
    return Event(
      Id: documentId,
      user: map["user"] as String,
      title: map["title"] as String,
      description: map["description"] as String,
      type: map["type"] as String?, // 修正字段大小写,与toMap保持一致
      from: (map["from"] as Timestamp).toDate(), // 转换Firestore Timestamp为DateTime
      to: (map["to"] as Timestamp).toDate(),
    );
  }
}

2. 修正database.dart的方法定义

class Database {
  // 实时监听事件数据(用于动态更新)
  Stream<QuerySnapshot> getEventsStream(String user) {
    return FirebaseFirestore.instance
        .collection(ApiPath.Event())
        .where("email", isEqualTo: user)
        .snapshots();
  }

  // 单次获取事件数据(用于静态加载)
  Future<QuerySnapshot> getEventsOnce(String user) async {
    return await FirebaseFirestore.instance
        .collection(ApiPath.Event())
        .where("email", isEqualTo: user)
        .get();
  }
}

3. 修正controller.dart的逻辑实现

class EventController {
  String get currentUser => Auth().getCurrentUserEmail();

  // 实时获取事件流(支持动态更新)
  Stream<List<Event>> getEventsStream() {
    return Database().getEventsStream(currentUser).map((querySnapshot) {
      return querySnapshot.docs.map((doc) {
        Map<String, dynamic> map = doc.data() as Map<String, dynamic>;
        return Event.fromMap(map, doc.id); // 使用doc.id获取Firestore文档ID
      }).toList();
    });
  }

  // 单次获取事件列表(仅加载一次)
  Future<List<Event>> getEventsOnce() async {
    QuerySnapshot querySnapshot = await Database().getEventsOnce(currentUser);
    return querySnapshot.docs.map((doc) {
      Map<String, dynamic> map = doc.data() as Map<String, dynamic>;
      return Event.fromMap(map, doc.id);
    }).toList();
  }
}

4. 修正Calendar.dart的异步数据处理

class Calendar extends StatefulWidget {
  const Calendar({super.key});

  @override
  State<Calendar> createState() => _CalendarState();
}

class _CalendarState extends State<Calendar> {
  final EventController _eventController = EventController();

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      height: double.infinity,
      child: Column(
        children: [
          Container(
            height: 700,
            margin: EdgeInsets.all(12),
            width: double.infinity,
            // 使用StreamBuilder处理实时数据
            child: StreamBuilder<List<Event>>(
              stream: _eventController.getEventsStream(),
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return const Center(child: CircularProgressIndicator());
                }
                if (snapshot.hasError) {
                  return Center(child: Text('加载错误: ${snapshot.error}'));
                }
                final events = snapshot.data ?? [];
                return SfCalendar(
                  view: CalendarView.month,
                  dataSource: EventDatasource(events),
                  initialSelectedDate: DateTime.now(),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

补充说明

  • 若不需要实时更新数据,可将StreamBuilder替换为FutureBuilder,并调用_eventController.getEventsOnce()方法。
  • 确保Firestore中from和to字段存储为Timestamp类型,避免DateTime转换失败。
  • EventDatasource无需修改,确保传入的是合法List<Event>即可。

内容的提问来源于stack exchange,提问作者Oula R.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 01:17:02