如何在Flutter中将icalendar_parser解析的JSON数据导入table_calendar实现Airbnb风格日历?
Flutter 将iCal JSON数据转换为table_calendar事件实现Airbnb式日历可用性展示
1. 定义table_calendar兼容的事件类
table_calendar需要自定义事件类存储核心数据,重点标记日期的可用性状态:
class CalendarEvent { final DateTime date; final bool isBooked; // 标记该日期是否已被预订(不可用) CalendarEvent({required this.date, required this.isBooked}); }
2. 解析iCal JSON数据生成事件列表
icalendar_parser转换后的JSON中,VEVENT数组包含所有预订记录,提取其中的DTSTART(入住日期)和DTEND(退房日期),将预订区间内的每一天标记为已预订:
List<CalendarEvent> convertIcalJsonToCalendarEvents(Map<String, dynamic> icalJson) { final List<CalendarEvent> events = []; final List<dynamic> vEvents = icalJson['VEVENT'] ?? []; for (final event in vEvents) { // 解析iCal日期字符串为DateTime对象 final DateTime startDate = DateTime.parse(event['DTSTART']); final DateTime endDate = DateTime.parse(event['DTEND']); // 遍历预订区间(退房日当天可用,循环到endDate前一天) DateTime currentDate = startDate; while (currentDate.isBefore(endDate)) { events.add(CalendarEvent(date: currentDate, isBooked: true)); currentDate = currentDate.add(const Duration(days: 1)); } } return events; }
3. 在table_calendar中集成事件数据
通过eventLoader关联事件,用calendarBuilders自定义单元格样式,区分可用/不可用日期:
// 初始化事件列表(假设已通过http获取并转换为icalJson) final List<CalendarEvent> calendarEvents = convertIcalJsonToCalendarEvents(icalJson); // 构建table_calendar TableCalendar( firstDay: DateTime.now().subtract(const Duration(days: 365)), lastDay: DateTime.now().add(const Duration(days: 365)), focusedDay: DateTime.now(), // 根据日期加载对应事件 eventLoader: (day) { return calendarEvents.where((event) => isSameDay(event.date, day)).toList(); }, // 自定义日期单元格样式 calendarBuilders: CalendarBuilders( defaultBuilder: (context, day, focusedDay) { final bool isBooked = calendarEvents.any((e) => isSameDay(e.date, day)); return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( color: isBooked ? Colors.grey[300] : Colors.green[100], borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( '${day.day}', style: TextStyle( color: isBooked ? Colors.grey[600] : Colors.green[800], ), ), ), ); }, ), // 可选:拦截已预订日期的点击 onDaySelected: (selectedDay, focusedDay) { final bool isBooked = calendarEvents.any((e) => isSameDay(e.date, selectedDay)); if (!isBooked) { // 处理可用日期的点击逻辑(如选择入住/退房) } }, )
关键注意事项
- 时区处理:部分iCal日期带时区后缀(如
Z表示UTC),DateTime.parse可直接解析;若为本地时间需根据业务调整时区转换。 - 重复事件:如果iCal包含重复预订(
RRULE字段),需确认icalendar_parser是否已将其展开为多个VEVENT,若未处理则需额外解析重复规则生成对应日期的事件。 - 边界日期:确保退房日不被标记为已预订,符合Airbnb的可用性逻辑。
内容的提问来源于stack exchange,提问作者Gede Destrawan
相关产品推荐
相关产品推荐

