| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | import 'package:flutter/cupertino.dart'; | 
					
						
							|  |  |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// 调用示例:
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  | /// DateTime? picked = await BottomDateTimePicker.showDate(
 | 
					
						
							|  |  |  |  | ///   context,
 | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  | ///   mode: BottomPickerMode.date, // 或 BottomPickerMode.dateTime(默认)或 BottomPickerMode.dateTimeWithSeconds
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  | ///   allowFuture: true,
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  | ///   allowPast: false, // 是否允许选择过去(false 表示只能选择现在或未来)
 | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  | ///   minTimeStr: '2025-08-20 08:30:45',
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  | /// );
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | /// if (picked != null) {
 | 
					
						
							|  |  |  |  | ///   print('用户选择的时间:$picked');
 | 
					
						
							|  |  |  |  | /// }
 | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  | enum BottomPickerMode { | 
					
						
							|  |  |  |  |   dateTime, // 底部弹窗 年月日时分
 | 
					
						
							|  |  |  |  |   date, // 中间弹窗日历
 | 
					
						
							|  |  |  |  |   dateTimeWithSeconds, // 底部弹窗 年月日时分秒
 | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | class BottomDateTimePicker { | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |   static Future<DateTime?> showDate( | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |     BuildContext context, { | 
					
						
							|  |  |  |  |     bool allowFuture = true, | 
					
						
							|  |  |  |  |     bool allowPast = true, // 是否允许选择过去(默认允许)
 | 
					
						
							|  |  |  |  |     String? minTimeStr, // 可选:'yyyy-MM-dd HH:mm:ss'
 | 
					
						
							|  |  |  |  |     BottomPickerMode mode = BottomPickerMode.dateTime, | 
					
						
							|  |  |  |  |   }) { | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |     return showModalBottomSheet<DateTime>( | 
					
						
							|  |  |  |  |       context: context, | 
					
						
							|  |  |  |  |       backgroundColor: Colors.white, | 
					
						
							|  |  |  |  |       isScrollControlled: true, | 
					
						
							|  |  |  |  |       shape: const RoundedRectangleBorder( | 
					
						
							|  |  |  |  |         borderRadius: BorderRadius.vertical(top: Radius.circular(12)), | 
					
						
							|  |  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       builder: | 
					
						
							|  |  |  |  |           (_) => _InlineDateTimePickerContent( | 
					
						
							|  |  |  |  |             allowFuture: allowFuture, | 
					
						
							|  |  |  |  |             allowPast: allowPast, | 
					
						
							|  |  |  |  |             minTimeStr: minTimeStr, | 
					
						
							|  |  |  |  |             mode: mode, | 
					
						
							|  |  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class _InlineDateTimePickerContent extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |   final bool allowFuture; | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |   final bool allowPast; | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |   final String? minTimeStr; | 
					
						
							|  |  |  |  |   final BottomPickerMode mode; | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |   const _InlineDateTimePickerContent({ | 
					
						
							|  |  |  |  |     Key? key, | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |     this.allowFuture = true, | 
					
						
							|  |  |  |  |     this.allowPast = true, | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     this.minTimeStr, | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     this.mode = BottomPickerMode.dateTime, | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |   }) : super(key: key); | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |   State<_InlineDateTimePickerContent> createState() => | 
					
						
							|  |  |  |  |       _InlineDateTimePickerContentState(); | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  | class _InlineDateTimePickerContentState | 
					
						
							|  |  |  |  |     extends State<_InlineDateTimePickerContent> { | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |   // 数据源
 | 
					
						
							|  |  |  |  |   final List<int> years = List.generate(101, (i) => 1970 + i); | 
					
						
							|  |  |  |  |   final List<int> months = List.generate(12, (i) => i + 1); | 
					
						
							|  |  |  |  |   final List<int> hours = List.generate(24, (i) => i); | 
					
						
							|  |  |  |  |   final List<int> minutes = List.generate(60, (i) => i); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |   final List<int> seconds = List.generate(60, (i) => i); // 新增秒数据源
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  |   // 动态天数列表(根据年月变化)
 | 
					
						
							|  |  |  |  |   late List<int> days; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |   // Controllers
 | 
					
						
							|  |  |  |  |   late FixedExtentScrollController yearCtrl; | 
					
						
							|  |  |  |  |   late FixedExtentScrollController monthCtrl; | 
					
						
							|  |  |  |  |   late FixedExtentScrollController dayCtrl; | 
					
						
							|  |  |  |  |   late FixedExtentScrollController hourCtrl; | 
					
						
							|  |  |  |  |   late FixedExtentScrollController minuteCtrl; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |   late FixedExtentScrollController secondCtrl; // 新增秒控制器
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 当前选中值
 | 
					
						
							|  |  |  |  |   late int selectedYear; | 
					
						
							|  |  |  |  |   late int selectedMonth; | 
					
						
							|  |  |  |  |   late int selectedDay; | 
					
						
							|  |  |  |  |   late int selectedHour; | 
					
						
							|  |  |  |  |   late int selectedMinute; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |   late int selectedSecond; // 新增秒选中值
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |   DateTime? _minTime; // 解析后的最小允许时间(如果有)
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |   @override | 
					
						
							|  |  |  |  |   void initState() { | 
					
						
							|  |  |  |  |     super.initState(); | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 解析 minTimeStr(若提供)
 | 
					
						
							|  |  |  |  |     _minTime = _parseMinTime(widget.minTimeStr); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |     // 初始时间:取 now 与 _minTime 的较大者(但要考虑 allowPast)
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |     final now = DateTime.now(); | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     DateTime initial = now; | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 如果指定了最小时间并且比 now 晚,则以最小时间为初始
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     if (_minTime != null && _minTime!.isAfter(initial)) { | 
					
						
							|  |  |  |  |       initial = _minTime!; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |     // 如果不允许选择过去,则确保 initial 至少为 now(或当天的 00:00,取决于模式)
 | 
					
						
							|  |  |  |  |     if (!widget.allowPast) { | 
					
						
							|  |  |  |  |       if (widget.mode == BottomPickerMode.date) { | 
					
						
							|  |  |  |  |         final today = DateTime(now.year, now.month, now.day); | 
					
						
							|  |  |  |  |         if (initial.isBefore(today)) initial = today; | 
					
						
							|  |  |  |  |       } else { | 
					
						
							|  |  |  |  |         if (initial.isBefore(now)) initial = now; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     // 根据模式调整初始值
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     if (widget.mode == BottomPickerMode.date) { | 
					
						
							|  |  |  |  |       initial = DateTime(initial.year, initial.month, initial.day); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     } else if (widget.mode == BottomPickerMode.dateTime) { | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       initial = DateTime( | 
					
						
							|  |  |  |  |         initial.year, | 
					
						
							|  |  |  |  |         initial.month, | 
					
						
							|  |  |  |  |         initial.day, | 
					
						
							|  |  |  |  |         initial.hour, | 
					
						
							|  |  |  |  |         initial.minute, | 
					
						
							|  |  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     // dateTimeWithSeconds 模式保持完整的时间
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     selectedYear = initial.year; | 
					
						
							|  |  |  |  |     selectedMonth = initial.month; | 
					
						
							|  |  |  |  |     selectedDay = initial.day; | 
					
						
							|  |  |  |  |     selectedHour = initial.hour; | 
					
						
							|  |  |  |  |     selectedMinute = initial.minute; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     selectedSecond = initial.second; | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  |     // 初始化天数列表
 | 
					
						
							|  |  |  |  |     days = _getDaysInMonth(selectedYear, selectedMonth); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     // controllers 初始项索引需在范围内
 | 
					
						
							|  |  |  |  |     yearCtrl = FixedExtentScrollController( | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       initialItem: years.indexOf(selectedYear).clamp(0, years.length - 1), | 
					
						
							|  |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     monthCtrl = FixedExtentScrollController( | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       initialItem: (selectedMonth - 1).clamp(0, months.length - 1), | 
					
						
							|  |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     dayCtrl = FixedExtentScrollController( | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       initialItem: (selectedDay - 1).clamp(0, days.length - 1), | 
					
						
							|  |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     hourCtrl = FixedExtentScrollController( | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       initialItem: selectedHour.clamp(0, hours.length - 1), | 
					
						
							|  |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     minuteCtrl = FixedExtentScrollController( | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       initialItem: selectedMinute.clamp(0, minutes.length - 1), | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |     secondCtrl = FixedExtentScrollController( | 
					
						
							|  |  |  |  |       // 初始化秒控制器
 | 
					
						
							|  |  |  |  |       initialItem: selectedSecond.clamp(0, seconds.length - 1), | 
					
						
							|  |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |     // 确保初始选择满足约束(例如 minTime 或禁止未来/禁止过去)
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     WidgetsBinding.instance.addPostFrameCallback((_) { | 
					
						
							|  |  |  |  |       _enforceConstraintsAndUpdateControllers(); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  |   // 根据年月获取当月天数
 | 
					
						
							|  |  |  |  |   List<int> _getDaysInMonth(int year, int month) { | 
					
						
							|  |  |  |  |     final lastDay = DateUtils.getDaysInMonth(year, month); | 
					
						
							|  |  |  |  |     return List.generate(lastDay, (i) => i + 1); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |   // 解析 'yyyy-MM-dd HH:mm:ss' 返回 DateTime 或 null
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |   DateTime? _parseMinTime(String? s) { | 
					
						
							|  |  |  |  |     if (s == null || s.trim().isEmpty) return null; | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       final trimmed = s.trim(); | 
					
						
							|  |  |  |  |       final parts = trimmed.split(' '); | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       final dateParts = parts[0].split('-').map((e) => int.parse(e)).toList(); | 
					
						
							|  |  |  |  |       final timeParts = | 
					
						
							|  |  |  |  |           (parts.length > 1) | 
					
						
							|  |  |  |  |               ? parts[1].split(':').map((e) => int.parse(e)).toList() | 
					
						
							|  |  |  |  |               : [0, 0, 0]; | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |       final year = dateParts[0]; | 
					
						
							|  |  |  |  |       final month = dateParts[1]; | 
					
						
							|  |  |  |  |       final day = dateParts[2]; | 
					
						
							|  |  |  |  |       final hour = (timeParts.isNotEmpty) ? timeParts[0] : 0; | 
					
						
							|  |  |  |  |       final minute = (timeParts.length > 1) ? timeParts[1] : 0; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |       final second = (timeParts.length > 2) ? timeParts[2] : 0; | 
					
						
							|  |  |  |  |       return DateTime(year, month, day, hour, minute, second); | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     } catch (e) { | 
					
						
							|  |  |  |  |       debugPrint('parseMinTime failed for "$s": $e'); | 
					
						
							|  |  |  |  |       return null; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  |   // 更新天数列表并调整选中日期
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |   void _updateDays({bool jumpDay = true}) { | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  |     final newDays = _getDaysInMonth(selectedYear, selectedMonth); | 
					
						
							|  |  |  |  |     final isDayValid = selectedDay <= newDays.length; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     setState(() { | 
					
						
							|  |  |  |  |       days = newDays; | 
					
						
							|  |  |  |  |       if (!isDayValid) { | 
					
						
							|  |  |  |  |         selectedDay = newDays.last; | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |         if (jumpDay) dayCtrl.jumpToItem(selectedDay - 1); | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |   // 检查并限制时间(模式感知),支持 allowPast 与 allowFuture
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |   void _enforceConstraintsAndUpdateControllers() { | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     final now = DateTime.now(); | 
					
						
							|  |  |  |  |     final isDateOnly = widget.mode == BottomPickerMode.date; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     final isDateTimeOnly = widget.mode == BottomPickerMode.dateTime; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     DateTime picked; | 
					
						
							|  |  |  |  |     if (isDateOnly) { | 
					
						
							|  |  |  |  |       picked = DateTime(selectedYear, selectedMonth, selectedDay); | 
					
						
							|  |  |  |  |     } else if (isDateTimeOnly) { | 
					
						
							|  |  |  |  |       picked = DateTime( | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |         selectedYear, | 
					
						
							|  |  |  |  |         selectedMonth, | 
					
						
							|  |  |  |  |         selectedDay, | 
					
						
							|  |  |  |  |         selectedHour, | 
					
						
							|  |  |  |  |         selectedMinute, | 
					
						
							|  |  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |       picked = DateTime( | 
					
						
							|  |  |  |  |         selectedYear, | 
					
						
							|  |  |  |  |         selectedMonth, | 
					
						
							|  |  |  |  |         selectedDay, | 
					
						
							|  |  |  |  |         selectedHour, | 
					
						
							|  |  |  |  |         selectedMinute, | 
					
						
							|  |  |  |  |         selectedSecond, | 
					
						
							|  |  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |     // 处理最小时间约束:结合 _minTime 与 allowPast
 | 
					
						
							|  |  |  |  |     DateTime? minRef; | 
					
						
							|  |  |  |  |     if (!widget.allowPast) { | 
					
						
							|  |  |  |  |       // 不允许选择过去:最小时间至少为 now(或当天 00:00)
 | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |       if (isDateOnly) { | 
					
						
							|  |  |  |  |         minRef = DateTime(now.year, now.month, now.day); | 
					
						
							|  |  |  |  |       } else if (isDateTimeOnly) { | 
					
						
							|  |  |  |  |         minRef = DateTime(now.year, now.month, now.day, now.hour, now.minute); | 
					
						
							|  |  |  |  |       } else { | 
					
						
							|  |  |  |  |         minRef = now; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |       // 如果用户也指定了 _minTime 且比 now 晚,则以 _minTime 为准
 | 
					
						
							|  |  |  |  |       if (_minTime != null && _minTime!.isAfter(minRef)) { | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |         minRef = _minTime; | 
					
						
							|  |  |  |  |         // 根据模式调整精度
 | 
					
						
							|  |  |  |  |         if (isDateOnly) { | 
					
						
							|  |  |  |  |           minRef = DateTime(minRef!.year, minRef.month, minRef.day); | 
					
						
							|  |  |  |  |         } else if (isDateTimeOnly) { | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |           minRef = DateTime( | 
					
						
							|  |  |  |  |             minRef!.year, | 
					
						
							|  |  |  |  |             minRef.month, | 
					
						
							|  |  |  |  |             minRef.day, | 
					
						
							|  |  |  |  |             minRef.hour, | 
					
						
							|  |  |  |  |             minRef.minute, | 
					
						
							|  |  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  |     } else if (_minTime != null) { | 
					
						
							|  |  |  |  |       // 允许选择过去,但若指定了 _minTime,则以 _minTime 为最小参考
 | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |       minRef = _minTime; | 
					
						
							|  |  |  |  |       // 根据模式调整精度
 | 
					
						
							|  |  |  |  |       if (isDateOnly) { | 
					
						
							|  |  |  |  |         minRef = DateTime(minRef!.year, minRef.month, minRef.day); | 
					
						
							|  |  |  |  |       } else if (isDateTimeOnly) { | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |         minRef = DateTime( | 
					
						
							|  |  |  |  |           minRef!.year, | 
					
						
							|  |  |  |  |           minRef.month, | 
					
						
							|  |  |  |  |           minRef.day, | 
					
						
							|  |  |  |  |           minRef.hour, | 
					
						
							|  |  |  |  |           minRef.minute, | 
					
						
							|  |  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |     if (minRef != null && picked.isBefore(minRef)) { | 
					
						
							|  |  |  |  |       // 把选中项调整为 minRef
 | 
					
						
							|  |  |  |  |       selectedYear = minRef.year; | 
					
						
							|  |  |  |  |       selectedMonth = minRef.month; | 
					
						
							|  |  |  |  |       selectedDay = minRef.day; | 
					
						
							|  |  |  |  |       if (!isDateOnly) { | 
					
						
							|  |  |  |  |         selectedHour = minRef.hour; | 
					
						
							|  |  |  |  |         selectedMinute = minRef.minute; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |         if (!isDateTimeOnly) { | 
					
						
							|  |  |  |  |           selectedSecond = minRef.second; | 
					
						
							|  |  |  |  |         } else { | 
					
						
							|  |  |  |  |           selectedSecond = 0; | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |       } else { | 
					
						
							|  |  |  |  |         selectedHour = 0; | 
					
						
							|  |  |  |  |         selectedMinute = 0; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |         selectedSecond = 0; | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       _updateDays(jumpDay: false); | 
					
						
							|  |  |  |  |       yearCtrl.jumpToItem(years.indexOf(selectedYear)); | 
					
						
							|  |  |  |  |       monthCtrl.jumpToItem(selectedMonth - 1); | 
					
						
							|  |  |  |  |       dayCtrl.jumpToItem(selectedDay - 1); | 
					
						
							|  |  |  |  |       if (!isDateOnly) { | 
					
						
							|  |  |  |  |         hourCtrl.jumpToItem(selectedHour); | 
					
						
							|  |  |  |  |         minuteCtrl.jumpToItem(selectedMinute); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |         if (!isDateTimeOnly) { | 
					
						
							|  |  |  |  |           secondCtrl.jumpToItem(selectedSecond); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     // 处理禁止选择未来(当 allowFuture == false)
 | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |     if (!widget.allowFuture) { | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |       DateTime nowRef; | 
					
						
							|  |  |  |  |       if (isDateOnly) { | 
					
						
							|  |  |  |  |         nowRef = DateTime(now.year, now.month, now.day); | 
					
						
							|  |  |  |  |       } else if (isDateTimeOnly) { | 
					
						
							|  |  |  |  |         nowRef = DateTime(now.year, now.month, now.day, now.hour, now.minute); | 
					
						
							|  |  |  |  |       } else { | 
					
						
							|  |  |  |  |         nowRef = now; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |       if (picked.isAfter(nowRef)) { | 
					
						
							|  |  |  |  |         selectedYear = nowRef.year; | 
					
						
							|  |  |  |  |         selectedMonth = nowRef.month; | 
					
						
							|  |  |  |  |         selectedDay = nowRef.day; | 
					
						
							|  |  |  |  |         if (!isDateOnly) { | 
					
						
							|  |  |  |  |           selectedHour = nowRef.hour; | 
					
						
							|  |  |  |  |           selectedMinute = nowRef.minute; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |           if (!isDateTimeOnly) { | 
					
						
							|  |  |  |  |             selectedSecond = nowRef.second; | 
					
						
							|  |  |  |  |           } else { | 
					
						
							|  |  |  |  |             selectedSecond = 0; | 
					
						
							|  |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |         } else { | 
					
						
							|  |  |  |  |           selectedHour = 0; | 
					
						
							|  |  |  |  |           selectedMinute = 0; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |           selectedSecond = 0; | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         _updateDays(jumpDay: false); | 
					
						
							|  |  |  |  |         yearCtrl.jumpToItem(years.indexOf(selectedYear)); | 
					
						
							|  |  |  |  |         monthCtrl.jumpToItem(selectedMonth - 1); | 
					
						
							|  |  |  |  |         dayCtrl.jumpToItem(selectedDay - 1); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |         if (!isDateOnly) { | 
					
						
							|  |  |  |  |           hourCtrl.jumpToItem(selectedHour); | 
					
						
							|  |  |  |  |           minuteCtrl.jumpToItem(selectedMinute); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |           if (!isDateTimeOnly) { | 
					
						
							|  |  |  |  |             secondCtrl.jumpToItem(selectedSecond); | 
					
						
							|  |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |         return; | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  |   @override | 
					
						
							|  |  |  |  |   void dispose() { | 
					
						
							|  |  |  |  |     yearCtrl.dispose(); | 
					
						
							|  |  |  |  |     monthCtrl.dispose(); | 
					
						
							|  |  |  |  |     dayCtrl.dispose(); | 
					
						
							|  |  |  |  |     hourCtrl.dispose(); | 
					
						
							|  |  |  |  |     minuteCtrl.dispose(); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     secondCtrl.dispose(); // 释放秒控制器
 | 
					
						
							| 
									
										
										
										
											2025-08-14 18:14:15 +08:00
										 |  |  |  |     super.dispose(); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |   @override | 
					
						
							|  |  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |     final isDateOnly = widget.mode == BottomPickerMode.date; | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |     final isDateTimeOnly = widget.mode == BottomPickerMode.dateTime; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 根据模式计算高度
 | 
					
						
							|  |  |  |  |     final height = isDateOnly ? 280 : (isDateTimeOnly ? 330 : 380); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |     return SizedBox( | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |       height: height.toDouble(), | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |       child: Column( | 
					
						
							|  |  |  |  |         children: [ | 
					
						
							|  |  |  |  |           // 顶部按钮
 | 
					
						
							|  |  |  |  |           Padding( | 
					
						
							|  |  |  |  |             padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), | 
					
						
							|  |  |  |  |             child: Row( | 
					
						
							|  |  |  |  |               mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
					
						
							|  |  |  |  |               children: [ | 
					
						
							|  |  |  |  |                 TextButton( | 
					
						
							|  |  |  |  |                   onPressed: () => Navigator.of(context).pop(), | 
					
						
							|  |  |  |  |                   child: const Text("取消", style: TextStyle(color: Colors.grey)), | 
					
						
							|  |  |  |  |                 ), | 
					
						
							|  |  |  |  |                 TextButton( | 
					
						
							|  |  |  |  |                   onPressed: () { | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |                     DateTime result; | 
					
						
							|  |  |  |  |                     if (isDateOnly) { | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |                       result = DateTime( | 
					
						
							|  |  |  |  |                         selectedYear, | 
					
						
							|  |  |  |  |                         selectedMonth, | 
					
						
							|  |  |  |  |                         selectedDay, | 
					
						
							|  |  |  |  |                       ); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |                     } else if (isDateTimeOnly) { | 
					
						
							|  |  |  |  |                       result = DateTime( | 
					
						
							|  |  |  |  |                         selectedYear, | 
					
						
							|  |  |  |  |                         selectedMonth, | 
					
						
							|  |  |  |  |                         selectedDay, | 
					
						
							|  |  |  |  |                         selectedHour, | 
					
						
							|  |  |  |  |                         selectedMinute, | 
					
						
							|  |  |  |  |                       ); | 
					
						
							|  |  |  |  |                     } else { | 
					
						
							|  |  |  |  |                       result = DateTime( | 
					
						
							|  |  |  |  |                         selectedYear, | 
					
						
							|  |  |  |  |                         selectedMonth, | 
					
						
							|  |  |  |  |                         selectedDay, | 
					
						
							|  |  |  |  |                         selectedHour, | 
					
						
							|  |  |  |  |                         selectedMinute, | 
					
						
							|  |  |  |  |                         selectedSecond, | 
					
						
							|  |  |  |  |                       ); | 
					
						
							|  |  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |                     Navigator.of(context).pop(result); | 
					
						
							|  |  |  |  |                   }, | 
					
						
							|  |  |  |  |                   child: const Text("确定", style: TextStyle(color: Colors.blue)), | 
					
						
							|  |  |  |  |                 ), | 
					
						
							|  |  |  |  |               ], | 
					
						
							|  |  |  |  |             ), | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |           const Divider(height: 1), | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |           // 可见的滚轮列
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |           Expanded( | 
					
						
							|  |  |  |  |             child: Row( | 
					
						
							|  |  |  |  |               children: [ | 
					
						
							|  |  |  |  |                 // 年
 | 
					
						
							|  |  |  |  |                 _buildPicker( | 
					
						
							|  |  |  |  |                   controller: yearCtrl, | 
					
						
							|  |  |  |  |                   items: years.map((e) => e.toString()).toList(), | 
					
						
							|  |  |  |  |                   onSelected: (idx) { | 
					
						
							|  |  |  |  |                     setState(() { | 
					
						
							|  |  |  |  |                       selectedYear = years[idx]; | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |                       _updateDays(); | 
					
						
							|  |  |  |  |                       _enforceConstraintsAndUpdateControllers(); | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |                     }); | 
					
						
							|  |  |  |  |                   }, | 
					
						
							|  |  |  |  |                 ), | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // 月
 | 
					
						
							|  |  |  |  |                 _buildPicker( | 
					
						
							|  |  |  |  |                   controller: monthCtrl, | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |                   items: | 
					
						
							|  |  |  |  |                       months.map((e) => e.toString().padLeft(2, '0')).toList(), | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |                   onSelected: (idx) { | 
					
						
							|  |  |  |  |                     setState(() { | 
					
						
							|  |  |  |  |                       selectedMonth = months[idx]; | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |                       _updateDays(); | 
					
						
							|  |  |  |  |                       _enforceConstraintsAndUpdateControllers(); | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |                     }); | 
					
						
							|  |  |  |  |                   }, | 
					
						
							|  |  |  |  |                 ), | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // 日
 | 
					
						
							|  |  |  |  |                 _buildPicker( | 
					
						
							|  |  |  |  |                   controller: dayCtrl, | 
					
						
							|  |  |  |  |                   items: days.map((e) => e.toString().padLeft(2, '0')).toList(), | 
					
						
							|  |  |  |  |                   onSelected: (idx) { | 
					
						
							|  |  |  |  |                     setState(() { | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  |                       final safeIdx = idx.clamp(0, days.length - 1); | 
					
						
							|  |  |  |  |                       selectedDay = days[safeIdx]; | 
					
						
							|  |  |  |  |                       _enforceConstraintsAndUpdateControllers(); | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |                     }); | 
					
						
							|  |  |  |  |                   }, | 
					
						
							|  |  |  |  |                 ), | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |                 // 若不是 dateOnly,则显示时分两列
 | 
					
						
							|  |  |  |  |                 if (!isDateOnly) | 
					
						
							|  |  |  |  |                   _buildPicker( | 
					
						
							|  |  |  |  |                     controller: hourCtrl, | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |                     items: | 
					
						
							|  |  |  |  |                         hours.map((e) => e.toString().padLeft(2, '0')).toList(), | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |                     onSelected: (idx) { | 
					
						
							|  |  |  |  |                       setState(() { | 
					
						
							|  |  |  |  |                         selectedHour = hours[idx]; | 
					
						
							|  |  |  |  |                         _enforceConstraintsAndUpdateControllers(); | 
					
						
							|  |  |  |  |                       }); | 
					
						
							|  |  |  |  |                     }, | 
					
						
							|  |  |  |  |                   ), | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 if (!isDateOnly) | 
					
						
							|  |  |  |  |                   _buildPicker( | 
					
						
							|  |  |  |  |                     controller: minuteCtrl, | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |                     items: | 
					
						
							|  |  |  |  |                         minutes | 
					
						
							|  |  |  |  |                             .map((e) => e.toString().padLeft(2, '0')) | 
					
						
							|  |  |  |  |                             .toList(), | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |                     onSelected: (idx) { | 
					
						
							|  |  |  |  |                       setState(() { | 
					
						
							|  |  |  |  |                         selectedMinute = minutes[idx]; | 
					
						
							|  |  |  |  |                         _enforceConstraintsAndUpdateControllers(); | 
					
						
							|  |  |  |  |                       }); | 
					
						
							|  |  |  |  |                     }, | 
					
						
							|  |  |  |  |                   ), | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // 如果是 dateTimeWithSeconds 模式,显示秒列
 | 
					
						
							|  |  |  |  |                 if (widget.mode == BottomPickerMode.dateTimeWithSeconds) | 
					
						
							|  |  |  |  |                   _buildPicker( | 
					
						
							|  |  |  |  |                     controller: secondCtrl, | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  |                     items: | 
					
						
							|  |  |  |  |                         seconds | 
					
						
							|  |  |  |  |                             .map((e) => e.toString().padLeft(2, '0')) | 
					
						
							|  |  |  |  |                             .toList(), | 
					
						
							| 
									
										
										
										
											2025-09-02 08:43:05 +08:00
										 |  |  |  |                     onSelected: (idx) { | 
					
						
							|  |  |  |  |                       setState(() { | 
					
						
							|  |  |  |  |                         selectedSecond = seconds[idx]; | 
					
						
							|  |  |  |  |                         _enforceConstraintsAndUpdateControllers(); | 
					
						
							|  |  |  |  |                       }); | 
					
						
							|  |  |  |  |                     }, | 
					
						
							|  |  |  |  |                   ), | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |               ], | 
					
						
							|  |  |  |  |             ), | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |         ], | 
					
						
							|  |  |  |  |       ), | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   Widget _buildPicker({ | 
					
						
							|  |  |  |  |     required FixedExtentScrollController controller, | 
					
						
							|  |  |  |  |     required List<String> items, | 
					
						
							|  |  |  |  |     required ValueChanged<int> onSelected, | 
					
						
							|  |  |  |  |   }) { | 
					
						
							|  |  |  |  |     return Expanded( | 
					
						
							|  |  |  |  |       child: CupertinoPicker.builder( | 
					
						
							|  |  |  |  |         scrollController: controller, | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |         itemExtent: 40, | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  |         childCount: items.length, | 
					
						
							|  |  |  |  |         onSelectedItemChanged: onSelected, | 
					
						
							|  |  |  |  |         itemBuilder: (context, index) { | 
					
						
							|  |  |  |  |           return Center(child: Text(items[index])); | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |       ), | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-09-02 16:22:17 +08:00
										 |  |  |  | } |