Flutter中如何按日期、月份、年份区分即将进行与已完成会议?
问题分析
你当前的筛选逻辑存在本质问题:用dd/MM/yyyy格式的字符串做日期比较时,是按字典序而非实际日期顺序判断的。比如"05/10/2024"(10月5日)会被认为小于"20/09/2024"(9月20日),因为字符串首字符0 < 2,完全不符合实际日期逻辑。
要实现「会议开始前显示在即将进行页,结束后显示在已完成页」的需求,必须将日期+时间转成可正确比较的DateTime或Firestore的Timestamp类型,再进行筛选。
解决方案
推荐优先在Firestore端完成筛选(效率更高),以下分两种场景给出实现代码:
场景1:Firestore存储完整时间戳(推荐)
如果能修改Firestore字段,新增Start Timestamp和End Timestamp字段(类型设为Timestamp),直接存储会议的完整开始/结束时间,查询逻辑会非常简洁:
即将进行会议的Stream
StreamBuilder( stream: meeting .where("Meeting Type", isEqualTo: type) .where("Start Timestamp", isGreaterThanOrEqualTo: Timestamp.now()) .snapshots(), builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) { if (streamSnapshot.hasData) { QuerySnapshot? querySnapshot = streamSnapshot.data; List<QueryDocumentSnapshot> documents = querySnapshot!.docs; List<Map> items = documents.map((e) => { "Meeting Date": e['Meeting Date'], "Meeting Name": e['Meeting Name'], "Place": e['Place'], "Time From": e['Time From'], "To Time": e['To Time'] }).toList(); // 后续UI构建逻辑(比如ListView) return ListView.builder( itemCount: items.length, itemBuilder: (ctx, idx) => ListTile( title: Text(items[idx]['Meeting Name']), subtitle: Text("${items[idx]['Meeting Date']} ${items[idx]['Time From']} - ${items[idx]['To Time']}"), ), ); } return const CircularProgressIndicator(); }, )
已完成会议的Stream
StreamBuilder( stream: meeting .where("Meeting Type", isEqualTo: type) .where("End Timestamp", isLessThan: Timestamp.now()) .snapshots(), builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) { if (streamSnapshot.hasData) { QuerySnapshot? querySnapshot = streamSnapshot.data; List<QueryDocumentSnapshot> documents = querySnapshot!.docs; List<Map> items = documents.map((e) => { "Meeting Date": e['Meeting Date'], "Meeting Name": e['Meeting Name'], "Place": e['Place'], "Time From": e['Time From'], "To Time": e['To Time'] }).toList(); // 后续UI构建逻辑 return ListView.builder( itemCount: items.length, itemBuilder: (ctx, idx) => ListTile( title: Text(items[idx]['Meeting Name']), subtitle: Text("${items[idx]['Meeting Date']} ${items[idx]['Time From']} - ${items[idx]['To Time']}"), ), ); } return const CircularProgressIndicator(); }, )
场景2:无法修改Firestore字段(基于现有字符串字段处理)
如果必须保留原有的Meeting Date(dd/MM/yyyy字符串)、Time From、To Time字段,只能在客户端拉取所有数据后,转成DateTime再筛选:
即将进行会议的实现
StreamBuilder( stream: meeting .where("Meeting Type", isEqualTo: type) .snapshots(), builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) { if (streamSnapshot.hasData) { final now = DateTime.now(); final documents = streamSnapshot.data!.docs; // 筛选即将进行的会议:开始时间 >= 当前时间 final upcomingDocs = documents.where((doc) { // 解析会议日期 final dateParts = (doc['Meeting Date'] as String).split('/'); final meetingDate = DateTime( int.parse(dateParts[2]), int.parse(dateParts[1]), int.parse(dateParts[0]), ); // 解析开始时间 final timeParts = (doc['Time From'] as String).split(':'); final meetingStartTime = DateTime( meetingDate.year, meetingDate.month, meetingDate.day, int.parse(timeParts[0]), int.parse(timeParts[1]), ); return meetingStartTime.isAfter(now) || meetingStartTime.isAtSameMomentAs(now); }).toList(); List<Map> items = upcomingDocs.map((e) => { "Meeting Date": e['Meeting Date'], "Meeting Name": e['Meeting Name'], "Place": e['Place'], "Time From": e['Time From'], "To Time": e['To Time'] }).toList(); return ListView.builder( itemCount: items.length, itemBuilder: (ctx, idx) => ListTile( title: Text(items[idx]['Meeting Name']), subtitle: Text("${items[idx]['Meeting Date']} ${items[idx]['Time From']} - ${items[idx]['To Time']}"), ), ); } return const CircularProgressIndicator(); }, )
已完成会议的实现
StreamBuilder( stream: meeting .where("Meeting Type", isEqualTo: type) .snapshots(), builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) { if (streamSnapshot.hasData) { final now = DateTime.now(); final documents = streamSnapshot.data!.docs; // 筛选已完成的会议:结束时间 < 当前时间 final completedDocs = documents.where((doc) { // 解析会议日期 final dateParts = (doc['Meeting Date'] as String).split('/'); final meetingDate = DateTime( int.parse(dateParts[2]), int.parse(dateParts[1]), int.parse(dateParts[0]), ); // 解析结束时间 final timeParts = (doc['To Time'] as String).split(':'); final meetingEndTime = DateTime( meetingDate.year, meetingDate.month, meetingDate.day, int.parse(timeParts[0]), int.parse(timeParts[1]), ); return meetingEndTime.isBefore(now); }).toList(); List<Map> items = completedDocs.map((e) => { "Meeting Date": e['Meeting Date'], "Meeting Name": e['Meeting Name'], "Place": e['Place'], "Time From": e['Time From'], "To Time": e['To Time'] }).toList(); return ListView.builder( itemCount: items.length, itemBuilder: (ctx, idx) => ListTile( title: Text(items[idx]['Meeting Name']), subtitle: Text("${items[idx]['Meeting Date']} ${items[idx]['Time From']} - ${items[idx]['To Time']}"), ), ); } return const CircularProgressIndicator(); }, )
内容的提问来源于stack exchange,提问作者divya manickam
相关产品推荐
相关产品推荐

