Flutter TableCalendar v3.0.2 加载REST API数据按选中日期展示实现问题
实现方案
步骤1:补充状态变量
在你的页面State类中新增以下状态,用于存储接口返回的数据和日期-事件映射关系:
late Future<List<ObjectName>> _dataFuture; List<ObjectName> _allEvents = []; // 核心映射结构:key为日期(时分秒清零),value为对应日期的所有数据对象 Map<DateTime, List<ObjectName>> _eventMap = {}; @override void initState() { super.initState(); _dataFuture = getData(); }
步骤2:用FutureBuilder包裹整体布局
将原有的TableCalendar外层套FutureBuilder,用于处理接口加载状态、构建日期映射:
@override Widget build(BuildContext context) { return FutureBuilder<List<ObjectName>>( future: _dataFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text('加载失败:${snapshot.error}')); } // 接口请求成功,构建日期-事件映射 _allEvents = snapshot.data!; _eventMap.clear(); for (var event in _allEvents) { // 日期统一转成UTC且时分秒清零,避免时区问题导致匹配失败 final dateKey = DateTime.utc(event.date.year, event.date.month, event.date.day); if (_eventMap[dateKey] == null) _eventMap[dateKey] = []; _eventMap[dateKey]!.add(event); } // 返回列布局:上方日历 + 下方选中日期的数据展示 return Column( children: [ // 这里放修改后的TableCalendar _buildCalendar(), // 下方展示区域 const SizedBox(height: 16), _buildSelectedDayContent(), ], ); }, ); }
步骤3:修改TableCalendar配置
新增事件加载逻辑,让日历自动给有数据的日期加标记点,同时注意修改firstDay适配历史数据:
Widget _buildCalendar() { return TableCalendar( // 注意:原firstDay是当前时间,要展示2020年的数据需要改早 firstDay: DateTime(2020), lastDay: DateTime(DateTime.now().year + 10), focusedDay: _focusedDay, calendarFormat: _calendarFormat, // 新增:事件加载器,返回对应日期的所有数据 eventLoader: (day) { final dateKey = DateTime.utc(day.year, day.month, day.day); return _eventMap[dateKey] ?? []; }, selectedDayPredicate: (day) { return isSameDay(_selectedDay, day); }, onDaySelected: (selectedDay, focusedDay) { if (!isSameDay(_selectedDay, selectedDay)) { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; }); } }, onFormatChanged: (format) { if (_calendarFormat != format) { setState(() { _calendarFormat = format; }); } }, onPageChanged: (focusedDay) { _focusedDay = focusedDay; }, ); }
步骤4:实现选中日期的数据展示逻辑
Widget _buildSelectedDayContent() { if (_selectedDay == null) return const SizedBox(); final dateKey = DateTime.utc(_selectedDay!.year, _selectedDay!.month, _selectedDay!.day); final dayEvents = _eventMap[dateKey] ?? []; if (dayEvents.isEmpty) { return const Padding( padding: EdgeInsets.all(16), child: Text('当天暂无数据'), ); } return Expanded( child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: dayEvents.length, itemBuilder: (context, index) { final event = dayEvents[index]; return Card( child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(event.name, style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 4), Text(event.description), ], ), ), ); }, ), ); }
注意事项
- 确保你的
ObjectName类的fromJson方法正确解析了嵌套的date字段,示例结构如下:
class ObjectName { final String id; final String name; final String description; final DateTime date; ObjectName({ required this.id, required this.name, required this.description, required this.date, }); factory ObjectName.fromJson(Map<String, dynamic> json) { final dateJson = json['date'] as Map<String, dynamic>; return ObjectName( id: json['id'], name: json['name'], description: json['description'], date: DateTime(dateJson['year'], dateJson['month'], dateJson['day']), ); } }
- 所有日期匹配统一用UTC时间或者都用本地时间,避免时区差异导致
isSameDay判断出错。
内容的提问来源于stack exchange,提问作者xen
相关产品推荐
相关产品推荐

