import 'package:flutter/material.dart'; import 'package:lpinyin/lpinyin.dart'; import 'package:qhd_prevention/customWidget/search_bar_widget.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; // 人员模型类 class Person { final String id; final String name; final String group; Person({required this.id, required this.name, required this.group}); } // 选择结果类 class SelectionPersonResult { final List selectedPersons; final Map> groupedSelected; SelectionPersonResult({ required this.selectedPersons, required this.groupedSelected, }); } class PersonSelectionPage extends StatefulWidget { const PersonSelectionPage(this.oldList, {super.key}); final List oldList; @override State createState() => _PersonSelectionPageState(); } class _PersonSelectionPageState extends State { // 字母表(将#放在最后) final List alphabet = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#' ]; // 人员数据 late final Map> groupedPersons; // 存储排序后的分组键列表 late final List sortedGroupKeys; // 存储每个分组的GlobalKey final Map groupKeys = {}; final List> list = [ { 'id': 'p1', 'name': '赵', }, { 'id': 'p2', 'name': '钱', }, { 'id': 'p3', 'name': '算', }, { 'id': 'p4', 'name': '里', }, { 'id': 'p5', 'name': '周', }, { 'id': 'p6', 'name': '吴', }, { 'id': 'p7', 'name': '啊', }, { 'id': 'p8', 'name': '到', }, { 'id': 'p9', 'name': '噢', }, { 'id': 'p10', 'name': '饿', }, { 'id': 'p11', 'name': '不', }, { 'id': 'p12', 'name': '吃', }, { 'id': 'p13', 'name': '123', // 测试数字 }, { 'id': 'p14', 'name': '@@@', // 测试特殊字符 }, ]; // 选中状态 final Map selectedStates = {}; // 分组选中状态 final Map groupSelectedStates = {}; // 列表控制器 final ScrollController _scrollController = ScrollController(); final TextEditingController _searchController = TextEditingController(); // 存储每个分组在列表中的位置索引 late final Map _groupIndexMap = {}; // 创建oldList的ID集合,用于快速查找 late final Set _oldSelectedIds; @override void initState() { super.initState(); // 创建oldList的ID集合 _oldSelectedIds = widget.oldList.map((person) => person.id).toSet(); _initData(); } void _initData() { // 根据姓名首字母分组 groupedPersons = {}; for (var item in list) { String name = item['name']; String id = item['id']; // 使用lpinyin获取姓名首字母 String firstLetter = _getFirstLetter(name); // 创建Person对象 Person person = Person( id: id, name: name, group: firstLetter, ); // 添加到对应分组 if (!groupedPersons.containsKey(firstLetter)) { groupedPersons[firstLetter] = []; } groupedPersons[firstLetter]!.add(person); } // 对每个分组内的人员按姓名排序 groupedPersons.forEach((key, value) { value.sort((a, b) => a.name.compareTo(b.name)); }); // 对分组键进行排序(确保#在最后) sortedGroupKeys = groupedPersons.keys.toList()..sort((a, b) { // 如果a是#,a应该在后面 if (a == '#') return 1; // 如果b是#,b应该在后面 if (b == '#') return -1; // 其他情况正常排序 return a.compareTo(b); }); // 构建分组索引映射 for (int i = 0; i < sortedGroupKeys.length; i++) { _groupIndexMap[sortedGroupKeys[i]] = i; } // 初始化选中状态和为每个分组创建GlobalKey for (var group in groupedPersons.keys) { for (var person in groupedPersons[group]!) { // 检查当前人员是否在oldList中,如果在则设置为true selectedStates[person.id] = _oldSelectedIds.contains(person.id); } // 初始化分组选中状态 groupSelectedStates[group] = false; groupKeys[group] = GlobalKey(); } // 更新每个分组的全选状态 for (var group in groupedPersons.keys) { _updateGroupSelection(group); } } // 使用lpinyin获取姓名的首字母 String _getFirstLetter(String name) { if (name.isEmpty) return '#'; try { // 检查第一个字符是否是英文字母 String firstChar = name.substring(0, 1); RegExp englishLetter = RegExp(r'^[A-Za-z]$'); if (englishLetter.hasMatch(firstChar)) { return firstChar.toUpperCase(); } // 检查第一个字符是否是数字或特殊字符 RegExp nonChinese = RegExp(r'^[0-9!@#\$%^&*()_+\-=\[\]{};:"\\|,.<>\/?~`]'); if (nonChinese.hasMatch(firstChar)) { return '#'; } // 获取拼音 - 使用WITHOUT_TONE格式获取完整拼音 String pinyin = PinyinHelper.getPinyin(name, separator: ' ', format: PinyinFormat.WITHOUT_TONE); if (pinyin.isNotEmpty) { // 获取第一个字的拼音首字母 List pinyinParts = pinyin.split(' '); if (pinyinParts.isNotEmpty) { String firstCharPinyin = pinyinParts[0]; if (firstCharPinyin.isNotEmpty) { String firstLetter = firstCharPinyin.substring(0, 1).toUpperCase(); // 检查是否是英文字母 if (englishLetter.hasMatch(firstLetter)) { return firstLetter; } } } } } catch (e) { // 异常处理,返回# print('拼音转换错误: $e'); } // 如果无法识别,返回# return '#'; } // 滚动到指定分组 void _scrollToGroup(String letter) { if (!groupedPersons.containsKey(letter)) return; // 方法1:使用索引跳转(更精确) int? index = _groupIndexMap[letter]; if (index != null && _scrollController.hasClients) { // 计算大概的滚动位置(每个分组标题高度 + 每个人item高度) double position = 0; for (int i = 0; i < index; i++) { String group = sortedGroupKeys[i]; // 分组标题高度56,每个人item高度56 position += 56; // 分组标题高度 position += (groupedPersons[group]?.length ?? 0) * 56; // 人员列表高度 } _scrollController.animateTo( position, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); return; } // 方法2:使用GlobalKey(备选方案) final key = groupKeys[letter]; if (key?.currentContext != null) { Scrollable.ensureVisible( key!.currentContext!, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, alignment: 0, // 滚动到顶部 ); } } // 更新分组的全选状态 void _updateGroupSelection(String group) { final groupPersons = groupedPersons[group]!; final allSelected = groupPersons.every((person) => selectedStates[person.id] == true); final anySelected = groupPersons.any((person) => selectedStates[person.id] == true); setState(() { groupSelectedStates[group] = allSelected; }); } // 切换分组的全选/全不选 void _toggleGroupSelection(String group) { final newState = !(groupSelectedStates[group] ?? false); final groupPersons = groupedPersons[group]!; setState(() { groupSelectedStates[group] = newState; for (var person in groupPersons) { selectedStates[person.id] = newState; } }); } // 获取选中的人员 SelectionPersonResult _getSelectedResult() { List selectedPersons = []; Map> groupedSelected = {}; for (var group in groupedPersons.keys) { final groupPersons = groupedPersons[group]!; final selectedInGroup = groupPersons .where((person) => selectedStates[person.id] == true) .toList(); if (selectedInGroup.isNotEmpty) { groupedSelected[group] = selectedInGroup; selectedPersons.addAll(selectedInGroup); } } return SelectionPersonResult( selectedPersons: selectedPersons, groupedSelected: groupedSelected, ); } // 确认添加并返回 void _confirmSelection() { final result = _getSelectedResult(); Navigator.pop(context, result); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: MyAppbar(title: '选择人员', actions: [ TextButton( onPressed: () { _confirmSelection(); }, child: Text( "确认添加", style: TextStyle(color: Colors.white, fontSize: 16), ), ), ],), body: Column( children: [ Container( color: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8), child: SearchBarWidget( showResetButton: true, hintText: '请输入关键字', resetButtonText: '重置', onReset: () { FocusScope.of(context).unfocus(); _searchController.text = ''; _search(); }, onSearch: (text) { FocusScope.of(context).unfocus(); _search(); }, controller: _searchController, ), ), // 人员列表 Expanded( child: Row( children: [ // 左侧人员列表 Expanded( child: ListView.builder( controller: _scrollController, itemCount: sortedGroupKeys.length, itemBuilder: (context, index) { String group = sortedGroupKeys[index]; List persons = groupedPersons[group]!; return _buildGroupSection(group, persons); }, ), ), // 右侧字母导航 Container( width: 30, color: Colors.grey.shade50, child: ListView.builder( itemCount: alphabet.length, itemBuilder: (context, index) { final letter = alphabet[index]; final hasGroup = groupedPersons.containsKey(letter); return GestureDetector( onTap: hasGroup ? () => _scrollToGroup(letter) : null, child: Container( height: 30, alignment: Alignment.center, child: Text( letter, style: TextStyle( fontSize: 12, color: hasGroup ? Colors.blue : Colors.grey.shade400, fontWeight: hasGroup ? FontWeight.bold : FontWeight.normal, ), ), ), ); }, ), ), ], ), ), ], ), ); } // 构建分组区域 Widget _buildGroupSection(String group, List persons) { return Container( key: groupKeys[group], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 分组标题 Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), color: Colors.grey.shade100, height: 56, // 固定高度便于计算 child: Row( children: [ Text( group, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const Spacer(), ], ), ), // 人员列表 ...persons.map((person) => _buildPersonItem(person)), ], ), ); } // 构建人员项 Widget _buildPersonItem(Person person) { return Container( decoration: BoxDecoration( border: Border( bottom: BorderSide( color: Colors.grey.shade200, width: 0.5, ), ), ), height: 56, // 固定高度便于计算 child: CheckboxListTile( title: Text( person.name, style: const TextStyle(fontSize: 14), ), value: selectedStates[person.id] ?? false, onChanged: (bool? value) { setState(() { selectedStates[person.id] = value ?? false; _updateGroupSelection(person.group); }); }, activeColor: Colors.blue, controlAffinity: ListTileControlAffinity.leading, contentPadding: const EdgeInsets.symmetric(horizontal: 0), ), ); } void _search() { // searchKeywords = _searchController.text.trim(); // currentPage = 1; // list.clear(); // _fetchData(); } }