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

如何基于API响应在Flutter的TableCalendar组件中设置事件

Flutter 日历组件对接API请假事件实现方案

依赖准备

使用table_calendar组件实现基础日历能力,配套intl做日期格式化处理,在pubspec.yaml中添加以下依赖后执行flutter pub get拉取包:

dependencies:
  flutter:
    sdk: flutter
  table_calendar: ^3.0.9
  intl: ^0.18.1
  # 如项目已有统一网络请求封装可不用引入http
  http: ^1.1.0

接口数据解析

接口返回的leave_management数组需要做两层处理:一是把Jan 01, 1970格式的字符串日期转成DateTime类型;二是对同一天的重复请假记录做去重,避免重复渲染标记。最终把数据处理成日历组件需要的Map<DateTime, List<请假事件>>结构,日期key统一去掉时分秒、只保留年月日,避免日期匹配失败。
首先定义事件模型:

class LeaveEvent {
  final DateTime leaveDate;
  final String holidayType;
  final String holidayReason;

  LeaveEvent({
    required this.leaveDate,
    required this.holidayType,
    required this.holidayReason,
  });

  factory LeaveEvent.fromJson(Map<String, dynamic> json) {
    final DateFormat formatter = DateFormat('MMM dd, yyyy');
    return LeaveEvent(
      leaveDate: formatter.parse(json['leave_date']),
      holidayType: json['holiday_type'],
      holidayReason: json['holiday_reason'],
    );
  }
}

编写接口请求、数据归类方法:

Future<Map<DateTime, List<LeaveEvent>>> fetchLeaveEvents() async {
  // 替换为项目实际的接口请求逻辑
  final response = await http.get(Uri.parse('你的请假数据接口地址'));
  if (response.statusCode == 200) {
    final Map<String, dynamic> resData = jsonDecode(response.body);
    final List leaveRawList = resData['leave_management'] ?? [];
    
    final Map<DateTime, List<LeaveEvent>> eventMap = {};
    for (final item in leaveRawList) {
      final event = LeaveEvent.fromJson(item);
      final dateKey = DateTime(event.leaveDate.year, event.leaveDate.month, event.leaveDate.day);
      
      eventMap[dateKey] ??= [];
      // 同日期同类型同原因的重复数据跳过
      final isDuplicate = eventMap[dateKey]!.any((e) => 
        e.holidayType == event.holidayType && e.holidayReason == event.holidayReason
      );
      if (!isDuplicate) {
        eventMap[dateKey]!.add(event);
      }
    }
    return eventMap;
  } else {
    throw Exception('请假数据加载失败');
  }
}

日历组件核心配置

实现效果对齐需求:不同请假类型显示对应颜色的圆点标记、点击日期展示请假详情、支持月份切换。核心代码如下:

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

  @override
  State<LeaveCalendarPage> createState() => _LeaveCalendarPageState();
}

class _LeaveCalendarPageState extends State<LeaveCalendarPage> {
  late Future<Map<DateTime, List<LeaveEvent>>> _eventLoader;
  DateTime _focusedDay = DateTime.now();
  DateTime? _selectedDay;

  @override
  void initState() {
    super.initState();
    _selectedDay = _focusedDay;
    _eventLoader = fetchLeaveEvents();
  }

  List<LeaveEvent> _getDayEvents(DateTime day, Map<DateTime, List<LeaveEvent>> allEvents) {
    final key = DateTime(day.year, day.month, day.day);
    return allEvents[key] ?? [];
  }

  // 不同请假类型对应不同标记颜色,可根据业务调整
  Color _getMarkColor(String holidayType) {
    switch (holidayType) {
      case 'partialy_off':
        return Colors.orange;
      case 'full_off':
        return Colors.red;
      default:
        return Colors.blueAccent;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('请假日历')),
      body: FutureBuilder<Map<DateTime, List<LeaveEvent>>>(
        future: _eventLoader,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(child: Text('加载失败:${snapshot.error}'));
          }
          final allEvents = snapshot.data!;
          return Column(
            children: [
              TableCalendar(
                firstDay: DateTime.utc(2020, 1, 1),
                lastDay: DateTime.utc(2030, 12, 31),
                focusedDay: _focusedDay,
                selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
                eventLoader: (day) => _getDayEvents(day, allEvents),
                onDaySelected: (selectedDay, focusedDay) {
                  setState(() {
                    _selectedDay = selectedDay;
                    _focusedDay = focusedDay;
                  });
                },
                calendarStyle: const CalendarStyle(markersMaxCount: 3),
                calendarBuilders: CalendarBuilders(
                  // 自定义彩色圆点标记
                  markerBuilder: (context, day, events) {
                    if (events.isEmpty) return const SizedBox.shrink();
                    return Positioned(
                      bottom: 5,
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: events.map((e) => Container(
                          width: 8,
                          height: 8,
                          margin: const EdgeInsets.symmetric(horizontal: 1),
                          decoration: BoxDecoration(
                            shape: BoxShape.circle,
                            color: _getMarkColor((e as LeaveEvent).holidayType),
                          ),
                        )).toList(),
                      ),
                    );
                  },
                ),
              ),
              const Divider(height: 1),
              // 选中日期的请假详情列表
              Expanded(
                child: ListView.builder(
                  padding: const EdgeInsets.all(16),
                  itemCount: _getDayEvents(_selectedDay!, allEvents).length,
                  itemBuilder: (context, index) {
                    final event = _getDayEvents(_selectedDay!, allEvents)[index];
                    return Card(
                      margin: const EdgeInsets.only(bottom: 12),
                      child: ListTile(
                        leading: Icon(Icons.event_available, color: _getMarkColor(event.holidayType)),
                        title: Text(event.holidayType == 'partialy_off' ? '半天休假' : '全天休假'),
                        subtitle: Text('请假原因:${event.holidayReason}'),
                      ),
                    );
                  },
                ),
              )
            ],
          );
        },
      ),
    );
  }
}

适配调整提示

  • 如果需要给请假日期加整格背景高亮,在calendarBuilders的defaultBuilder中自定义单元格渲染逻辑即可
  • 圆点大小、日历文字样式、选中态颜色都可以通过CalendarStyle参数调整
  • 如需扩展补班、法定节假日等其他标记,直接在事件模型中新增类型,扩展_getMarkColor的映射逻辑即可

内容的提问来源于stack exchange,提问作者Abhay kumar bhumihar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:42:12