如何基于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
相关产品推荐
相关产品推荐

