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

