101 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			101 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Dart
		
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:flutter/cupertino.dart';
 | |
| import 'package:qhd_prevention/customWidget/toast_util.dart';
 | |
| 
 | |
| /// 通用底部弹窗选择器
 | |
| /// 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>(
 | |
|     BuildContext context, {
 | |
|     required List<T> items,
 | |
|     required Widget Function(T item) itemBuilder,
 | |
|     int initialIndex = 0,
 | |
|     double itemExtent = 50.0,
 | |
|     double height = 250,
 | |
|   }) {
 | |
|     if (items.isEmpty) return Future.value(null);
 | |
| 
 | |
|     // 确保初始索引合法
 | |
|     final safeIndex = initialIndex.clamp(0, items.length - 1);
 | |
|     // 当前选中项
 | |
|     T selected = items[safeIndex];
 | |
| 
 | |
|     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(
 | |
|                 padding: const EdgeInsets.symmetric(
 | |
|                   horizontal: 16,
 | |
|                   vertical: 8,
 | |
|                 ),
 | |
|                 child: Row(
 | |
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | |
|                   children: [
 | |
|                     TextButton(
 | |
|                       onPressed: () {
 | |
|                         FocusScope.of(context).unfocus();
 | |
|                         Navigator.of(ctx).pop();
 | |
|                       },
 | |
|                       child: const Text('取消', style: TextStyle(color: Colors.black54, fontSize: 16),),
 | |
|                     ),
 | |
|                     TextButton(
 | |
|                       onPressed: () {
 | |
|                         FocusScope.of(context).unfocus();
 | |
|                         Navigator.of(ctx).pop(selected);
 | |
|                       },
 | |
|                       child: const Text('确定', style: TextStyle(color: Colors.blue, fontSize: 16),),
 | |
|                     ),
 | |
|                   ],
 | |
|                 ),
 | |
|               ),
 | |
|               const Divider(height: 1),
 | |
|               // 滚动选择器
 | |
|               Expanded(
 | |
|                 child: CupertinoPicker(
 | |
|                   scrollController: FixedExtentScrollController(
 | |
|                     initialItem: initialIndex,
 | |
|                   ),
 | |
|                   itemExtent: itemExtent,
 | |
|                   onSelectedItemChanged: (index) {
 | |
|                     selected = items[index];
 | |
|                   },
 | |
|                   // 把 itemBuilder 返回的 Widget 用 Center 包一层
 | |
|                   children: items.map((item) => Center(child: itemBuilder(item))).toList(),
 | |
|                 ),
 | |
|               ),
 | |
| 
 | |
|             ],
 | |
|           ),
 | |
|         );
 | |
|       },
 | |
|     );
 | |
|   }
 | |
| }
 |