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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 01:37:01