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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:50:28