import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; /// 居中多选弹窗(Dialog) /// 返回 Future?>:用户点击确定返回所选项列表;取消或关闭返回 null。 class CenterMultiPicker { static Future?> show( BuildContext context, { required List items, required Widget Function(T item) itemBuilder, List? initialSelectedIndices, int? maxSelection, bool allowEmpty = false, double itemHeight = 52, double maxHeightFactor = 0.75, // 屏幕高度的最大占比 String? title, }) { if (items.isEmpty) return Future.value(null); // 安全化初始索引 final initialSet = {}; if (initialSelectedIndices != null) { for (final i in initialSelectedIndices) { // if (i >= 0 && i < items.length) initialSet.add(i); } } return showDialog?>( context: context, barrierDismissible: true, builder: (ctx) { return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), insetPadding: const EdgeInsets.symmetric( horizontal: 24, vertical: 24, ), child: _CenterMultiPickerBody( items: items, itemBuilder: itemBuilder, initialSelected: initialSet, maxSelection: maxSelection, allowEmpty: allowEmpty, itemHeight: itemHeight, maxHeightFactor: maxHeightFactor, title: title, ), ); }, ); } } class _CenterMultiPickerBody extends StatefulWidget { const _CenterMultiPickerBody({ Key? key, required this.items, required this.itemBuilder, required this.initialSelected, required this.maxSelection, required this.allowEmpty, required this.itemHeight, required this.maxHeightFactor, this.title, }) : super(key: key); final List items; final Widget Function(T item) itemBuilder; final Set initialSelected; final int? maxSelection; final bool allowEmpty; final double itemHeight; final double maxHeightFactor; final String? title; @override State<_CenterMultiPickerBody> createState() => _CenterMultiPickerBodyState(); } class _CenterMultiPickerBodyState extends State<_CenterMultiPickerBody> { late Set _selected; // 固定的 header / footer 高度估算 static const double _headerHeight = 56; static const double _footerHeight = 58; static const double _verticalPadding = 16; // Dialog 内上下 padding @override void initState() { super.initState(); _selected = Set.from(widget.initialSelected); } void _toggle(int idx) { setState(() { if (_selected.contains(idx)) { _selected.remove(idx); } else { if (widget.maxSelection != null && _selected.length >= widget.maxSelection!) { ToastUtil.showNormal(context, '最多可选择 ${widget.maxSelection} 项'); return; } _selected.add(idx); } }); } @override Widget build(BuildContext context) { final items = widget.items; final screenH = MediaQuery.of(context).size.height; final contentHeight = items.length * widget.itemHeight + _headerHeight + _footerHeight + _verticalPadding * 2; final maxAllowed = screenH * widget.maxHeightFactor; final dialogHeight = contentHeight <= maxAllowed ? contentHeight : maxAllowed; return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10) ), width: double.infinity, height: dialogHeight, child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ // header Container( height: _headerHeight, padding: const EdgeInsets.symmetric(horizontal: 16), alignment: Alignment.centerLeft, child: Row( children: [ if (widget.title != null) Expanded( child: Text( widget.title!, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, ), ), ) else Expanded( child: Text( '已选 ${_selected.length}${widget.maxSelection != null ? '/${widget.maxSelection}' : ''}', style: const TextStyle(fontSize: 15), ), ), // 可将一些快捷按钮放在右侧(如全选/反选),这里暂不显示 ], ), ), const Divider(height: 1), // 列表区域(可滚动) Expanded( child: Container( padding: const EdgeInsets.symmetric(vertical: 8), child: Scrollbar( thumbVisibility: true, child: ListView.separated( physics: const BouncingScrollPhysics(), itemCount: items.length, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (ctx, idx) { final isSelected = _selected.contains(idx); return InkWell( onTap: () => _toggle(idx), child: Container( height: widget.itemHeight, padding: const EdgeInsets.symmetric( horizontal: 14, vertical: 6, ), child: Row( children: [ Container( width: 22, height: 22, decoration: BoxDecoration( color: isSelected ? Colors.blue : Colors.transparent, border: Border.all( color: isSelected ? Colors.blue : Colors.black26, ), borderRadius: BorderRadius.circular(4), ), child: isSelected ? const Icon( Icons.check, size: 18, color: Colors.white, ) : null, ), const SizedBox(width: 12), Expanded(child: widget.itemBuilder(items[idx])), ], ), ), ); }, ), ), ), ), const Divider(height: 1), // footer: 取消 / 确定(固定在底部) Container( height: _footerHeight, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ Expanded( child: CustomButton( text: '取消', backgroundColor: Colors.grey.shade200, textStyle: TextStyle(fontSize: 14, color: Colors.black), onPressed: () { Navigator.of(context).pop(null); }, ), ), const SizedBox(width: 12), Expanded( child: CustomButton( text: '确定', backgroundColor: Colors.blue, onPressed: () { if (!widget.allowEmpty && _selected.isEmpty) { ToastUtil.showNormal(context, '请至少选择一项'); return; } final result = _selected .map((i) => items[i]) .toList(growable: false); Navigator.of(context).pop(result); }, ), ), ], ), ), ], ), ); } }