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

如何在Flutter应用中添加带三列的下拉式TimePicker?

Flutter 下拉式三列时间选择器实现方案

方案一:自定义下拉展开式三列选择器(基于Dropdown组件)

这种方案通过原生DropdownButton组合实现,点击触发按钮后展开包含小时、分钟、时段的三列选择面板。

实现代码:

import 'package:flutter/material.dart';

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

  @override
  State<DropdownTimePicker> createState() => _DropdownTimePickerState();
}

class _DropdownTimePickerState extends State<DropdownTimePicker> {
  // 状态变量
  String selectedHour = '1';
  String selectedMinute = '00';
  String selectedPeriod = 'AM';
  bool isExpanded = false;

  // 生成小时选项(1-12)
  final List<String> hourOptions = List.generate(12, (index) => (index + 1).toString());
  // 生成分钟选项(00-59,补零)
  final List<String> minuteOptions = List.generate(60, (index) => index.toString().padLeft(2, '0'));
  // 时段选项
  final List<String> periodOptions = ['AM', 'PM'];

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        // 触发按钮
        TextButton(
          onPressed: () {
            setState(() {
              isExpanded = !isExpanded;
            });
          },
          child: Text(
            '$selectedHour:$selectedMinute $selectedPeriod',
            style: const TextStyle(fontSize: 16),
          ),
        ),
        // 展开的三列选择器
        if (isExpanded)
          Container(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            decoration: BoxDecoration(
              border: Border.all(color: Colors.grey.shade300),
              borderRadius: BorderRadius.circular(8),
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                // 小时选择列
                DropdownButton<String>(
                  value: selectedHour,
                  items: hourOptions.map((hour) {
                    return DropdownMenuItem(
                      value: hour,
                      child: Text(hour),
                    );
                  }).toList(),
                  onChanged: (value) {
                    setState(() {
                      selectedHour = value!;
                    });
                  },
                ),
                const Text(':'),
                // 分钟选择列
                DropdownButton<String>(
                  value: selectedMinute,
                  items: minuteOptions.map((minute) {
                    return DropdownMenuItem(
                      value: minute,
                      child: Text(minute),
                    );
                  }).toList(),
                  onChanged: (value) {
                    setState(() {
                      selectedMinute = value!;
                    });
                  },
                ),
                // 时段选择列
                DropdownButton<String>(
                  value: selectedPeriod,
                  items: periodOptions.map((period) {
                    return DropdownMenuItem(
                      value: period,
                      child: Text(period),
                    );
                  }).toList(),
                  onChanged: (value) {
                    setState(() {
                      selectedPeriod = value!;
                    });
                  },
                ),
              ],
            ),
          ),
      ],
    );
  }
}

// 使用示例
void main() {
  runApp(const MaterialApp(
    home: Scaffold(
      body: Center(child: DropdownTimePicker()),
    ),
  ));
}

关键说明:

  • 通过isExpanded布尔值控制选择面板的显示/隐藏
  • 预先生成小时、分钟、时段的选项列表,避免重复创建
  • 三列选择器通过Row横向排列,保持布局紧凑

方案二:底部弹窗式三列时间选择器(更友好的交互)

如果觉得下拉展开的布局过于拥挤,可以采用底部弹窗的形式,结合CupertinoPicker实现多列滚动选择,交互体验更流畅。

实现代码:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

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

  @override
  State<BottomSheetTimePicker> createState() => _BottomSheetTimePickerState();
}

class _BottomSheetTimePickerState extends State<BottomSheetTimePicker> {
  int selectedHourIndex = 0;
  int selectedMinuteIndex = 0;
  int selectedPeriodIndex = 0;

  final List<String> hours = List.generate(12, (index) => (index + 1).toString());
  final List<String> minutes = List.generate(60, (index) => index.toString().padLeft(2, '0'));
  final List<String> periods = ['AM', 'PM'];

  String get selectedTime => '${hours[selectedHourIndex]}:${minutes[selectedMinuteIndex]} ${periods[selectedPeriodIndex]}';

  void _showTimePicker() {
    showModalBottomSheet(
      context: context,
      builder: (context) {
        return SizedBox(
          height: 250,
          child: Column(
            children: [
              // 顶部操作栏
              Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  TextButton(
                    onPressed: () => Navigator.pop(context),
                    child: const Text('取消'),
                  ),
                  TextButton(
                    onPressed: () {
                      setState(() {});
                      Navigator.pop(context);
                    },
                    child: const Text('确认'),
                  ),
                ],
              ),
              // 三列滚动选择器
              Expanded(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    // 小时列
                    SizedBox(
                      width: 80,
                      child: CupertinoPicker(
                        itemExtent: 40,
                        onSelectedItemChanged: (index) {
                          setState(() {
                            selectedHourIndex = index;
                          });
                        },
                        children: hours.map((hour) => Center(child: Text(hour))).toList(),
                      ),
                    ),
                    const Center(child: Text(':', style: TextStyle(fontSize: 20))),
                    // 分钟列
                    SizedBox(
                      width: 80,
                      child: CupertinoPicker(
                        itemExtent: 40,
                        onSelectedItemChanged: (index) {
                          setState(() {
                            selectedMinuteIndex = index;
                          });
                        },
                        children: minutes.map((minute) => Center(child: Text(minute))).toList(),
                      ),
                    ),
                    // 时段列
                    SizedBox(
                      width: 80,
                      child: CupertinoPicker(
                        itemExtent: 40,
                        onSelectedItemChanged: (index) {
                          setState(() {
                            selectedPeriodIndex = index;
                          });
                        },
                        children: periods.map((period) => Center(child: Text(period))).toList(),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: TextButton(
        onPressed: _showTimePicker,
        child: Text(
          selectedTime,
          style: const TextStyle(fontSize: 18),
        ),
      ),
    );
  }
}

// 使用示例
void main() {
  runApp(const MaterialApp(
    home: Scaffold(
      body: BottomSheetTimePicker(),
    ),
  ));
}

关键说明:

  • 利用showModalBottomSheet弹出底部选择面板,避免页面布局混乱
  • CupertinoPicker原生支持滚动选择,适配时间选择的交互习惯
  • 通过索引变量跟踪选中项,确认后更新界面显示

内容的提问来源于stack exchange,提问作者Honey Last

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 04:52:47