如何使用Flutter/Dart实现该设计中的可用时段选择器
Flutter 可用时段选择器实现方案
核心实现思路
将一天划分为「上午/下午/晚上」等时段组,每个组内包含若干可选时间区间按钮,通过状态管理维护选中状态,支持单选/多选切换,完全匹配附图的交互样式。
完整代码实现
1. 定义数据模型
// 时段组模型(比如"上午"组) class TimeSlotGroup { final String title; final List<TimeSlot> slots; TimeSlotGroup({required this.title, required this.slots}); } // 单个时段模型(比如"9:00-10:00") class TimeSlot { final String label; final bool isSelected; TimeSlot({required this.label, this.isSelected = false}); // 用于更新选中状态的复制方法 TimeSlot copyWith({bool? isSelected}) { return TimeSlot( label: label, isSelected: isSelected ?? this.isSelected, ); } }
2. 主选择器组件
class TimeSlotSelector extends StatefulWidget { const TimeSlotSelector({super.key}); @override State<TimeSlotSelector> createState() => _TimeSlotSelectorState(); } class _TimeSlotSelectorState extends State<TimeSlotSelector> { late List<TimeSlotGroup> _timeSlotGroups; @override void initState() { super.initState(); // 初始化时段数据(可替换为接口请求结果) _timeSlotGroups = [ TimeSlotGroup( title: "上午", slots: [ TimeSlot(label: "9:00-10:00"), TimeSlot(label: "10:00-11:00"), TimeSlot(label: "11:00-12:00"), ], ), TimeSlotGroup( title: "下午", slots: [ TimeSlot(label: "13:00-14:00"), TimeSlot(label: "14:00-15:00"), TimeSlot(label: "15:00-16:00"), TimeSlot(label: "16:00-17:00"), ], ), TimeSlotGroup( title: "晚上", slots: [ TimeSlot(label: "18:00-19:00"), TimeSlot(label: "19:00-20:00"), TimeSlot(label: "20:00-21:00"), ], ), ]; } // 切换时段选中状态(默认同组单选,可改为多选) void _toggleSlotSelection(int groupIndex, int slotIndex) { setState(() { final updatedSlots = _timeSlotGroups[groupIndex].slots.map((slot) { // 单选逻辑:同组仅保留当前选中项 if (slot == _timeSlotGroups[groupIndex].slots[slotIndex]) { return slot.copyWith(isSelected: !slot.isSelected); } else { return slot.copyWith(isSelected: false); } // 多选逻辑:仅切换当前项状态,注释掉上面的else分支即可 // return slot == _timeSlotGroups[groupIndex].slots[slotIndex] // ? slot.copyWith(isSelected: !slot.isSelected) // : slot; }).toList(); _timeSlotGroups[groupIndex] = TimeSlotGroup( title: _timeSlotGroups[groupIndex].title, slots: updatedSlots, ); }); } @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: _timeSlotGroups.map((group) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 时段组标题 Padding( padding: const EdgeInsets.only(bottom: 12), child: Text( group.title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), // 时段按钮网格 Wrap( spacing: 12, runSpacing: 12, children: group.slots.asMap().entries.map((entry) { final slotIndex = entry.key; final slot = entry.value; return GestureDetector( onTap: () => _toggleSlotSelection( _timeSlotGroups.indexOf(group), slotIndex, ), child: Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), decoration: BoxDecoration( color: slot.isSelected ? Colors.blue[600] : Colors.grey[200], borderRadius: BorderRadius.circular(8), ), child: Text( slot.label, style: TextStyle( color: slot.isSelected ? Colors.white : Colors.black87, fontSize: 14, ), ), ), ); }).toList(), ), const SizedBox(height: 20), ], ); }).toList(), ), ); } }
自定义调整说明
- 样式适配:可修改
BoxDecoration的颜色、圆角,以及Text的字体样式,完全匹配设计图的视觉效果 - 数据来源:将
initState中的静态数据替换为后端接口返回的时段列表 - 选中逻辑:注释掉单选逻辑的else分支,即可切换为多选模式;还可添加最多选中N个时段的限制逻辑
内容的提问来源于stack exchange,提问作者irfan ap
相关产品推荐
相关产品推荐

