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.
相关产品推荐
相关产品推荐

