| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:flutter/cupertino.dart'; | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  | import 'package:qhd_prevention/customWidget/toast_util.dart'; | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /// 通用底部弹窗选择器
 | 
					
						
							|  |  |  | /// Example:
 | 
					
						
							|  |  |  | /// ```dart
 | 
					
						
							|  |  |  | /// final choice = await BottomPicker.show<String>(
 | 
					
						
							|  |  |  | ///   context,
 | 
					
						
							|  |  |  | ///   items: ['选项1', '选项2', '选项3'],
 | 
					
						
							|  |  |  | ///   itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
 | 
					
						
							|  |  |  | ///   initialIndex: 1,
 | 
					
						
							|  |  |  | /// );
 | 
					
						
							|  |  |  | /// if (choice != null) {
 | 
					
						
							|  |  |  | ///   // 用户点击确定并选择了 choice
 | 
					
						
							|  |  |  | /// }
 | 
					
						
							|  |  |  | /// ```
 | 
					
						
							|  |  |  | class BottomPicker { | 
					
						
							|  |  |  |   /// 显示底部选择器弹窗
 | 
					
						
							|  |  |  |   ///
 | 
					
						
							|  |  |  |   /// [items]: 选项列表
 | 
					
						
							|  |  |  |   /// [itemBuilder]: 每个选项的展示 Widget
 | 
					
						
							|  |  |  |   /// [initialIndex]: 初始选中索引
 | 
					
						
							|  |  |  |   /// [itemExtent]: 列表行高
 | 
					
						
							|  |  |  |   /// [height]: 弹窗总高度
 | 
					
						
							|  |  |  |   static Future<T?> show<T>( | 
					
						
							| 
									
										
										
										
											2025-08-01 16:41:44 +08:00
										 |  |  |     BuildContext context, { | 
					
						
							|  |  |  |     required List<T> items, | 
					
						
							|  |  |  |     required Widget Function(T item) itemBuilder, | 
					
						
							|  |  |  |     int initialIndex = 0, | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |     double itemExtent = 50.0, | 
					
						
							| 
									
										
										
										
											2025-08-01 16:41:44 +08:00
										 |  |  |     double height = 250, | 
					
						
							|  |  |  |   }) { | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |     if (items.isEmpty) return Future.value(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 确保初始索引合法
 | 
					
						
							|  |  |  |     final safeIndex = initialIndex.clamp(0, items.length - 1); | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |     // 当前选中项
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |     T selected = items[safeIndex]; | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return showModalBottomSheet<T>( | 
					
						
							|  |  |  |       context: context, | 
					
						
							|  |  |  |       backgroundColor: Colors.white, | 
					
						
							|  |  |  |       shape: const RoundedRectangleBorder( | 
					
						
							|  |  |  |         borderRadius: BorderRadius.vertical(top: Radius.circular(12)), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       builder: (ctx) { | 
					
						
							|  |  |  |         return SizedBox( | 
					
						
							|  |  |  |           height: height, | 
					
						
							|  |  |  |           child: Column( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               // 按钮行
 | 
					
						
							|  |  |  |               Padding( | 
					
						
							| 
									
										
										
										
											2025-08-01 16:41:44 +08:00
										 |  |  |                 padding: const EdgeInsets.symmetric( | 
					
						
							|  |  |  |                   horizontal: 16, | 
					
						
							|  |  |  |                   vertical: 8, | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |                 child: Row( | 
					
						
							|  |  |  |                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     TextButton( | 
					
						
							| 
									
										
										
										
											2025-08-01 16:41:44 +08:00
										 |  |  |                       onPressed: () { | 
					
						
							|  |  |  |                         FocusScope.of(context).unfocus(); | 
					
						
							|  |  |  |                         Navigator.of(ctx).pop(); | 
					
						
							|  |  |  |                       }, | 
					
						
							| 
									
										
										
										
											2025-08-14 15:05:48 +08:00
										 |  |  |                       child: const Text('取消', style: TextStyle(color: Colors.black54, fontSize: 16),), | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |                     ), | 
					
						
							|  |  |  |                     TextButton( | 
					
						
							| 
									
										
										
										
											2025-08-01 16:41:44 +08:00
										 |  |  |                       onPressed: () { | 
					
						
							|  |  |  |                         FocusScope.of(context).unfocus(); | 
					
						
							|  |  |  |                         Navigator.of(ctx).pop(selected); | 
					
						
							|  |  |  |                       }, | 
					
						
							| 
									
										
										
										
											2025-08-14 15:05:48 +08:00
										 |  |  |                       child: const Text('确定', style: TextStyle(color: Colors.blue, fontSize: 16),), | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |                     ), | 
					
						
							|  |  |  |                   ], | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |               const Divider(height: 1), | 
					
						
							|  |  |  |               // 滚动选择器
 | 
					
						
							|  |  |  |               Expanded( | 
					
						
							|  |  |  |                 child: CupertinoPicker( | 
					
						
							| 
									
										
										
										
											2025-08-01 16:41:44 +08:00
										 |  |  |                   scrollController: FixedExtentScrollController( | 
					
						
							|  |  |  |                     initialItem: initialIndex, | 
					
						
							|  |  |  |                   ), | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |                   itemExtent: itemExtent, | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |                   onSelectedItemChanged: (index) { | 
					
						
							|  |  |  |                     selected = items[index]; | 
					
						
							|  |  |  |                   }, | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |                   // 把 itemBuilder 返回的 Widget 用 Center 包一层
 | 
					
						
							|  |  |  |                   children: items.map((item) => Center(child: itemBuilder(item))).toList(), | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |             ], | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |