flutter_integrated_whb/lib/customWidget/department_person_picker.dart

152 lines
6.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
/// 用户数据模型
class Person {
final String userId;
final String name;
Person({required this.userId, required this.name});
factory Person.fromJson(Map<String, dynamic> json) {
return Person(
userId: json['USER_ID'] as String,
name: json['NAME'] as String,
);
}
}
/// 原回调签名(向后兼容)
typedef PersonSelectCallback = void Function(String userId, String name);
/// 新回调签名,增加可选 indexint默认 0
typedef PersonSelectCallbackWithIndex = void Function(String userId, String name, int index);
/// 底部弹窗人员选择器(使用预先传入的原始数据列表,不做接口请求)
class DepartmentPersonPicker {
/// 显示人员选择弹窗
///
/// [personsData]: 已拉取并缓存的原始 Map 列表
/// [onSelected]: 选中后回调 USER_ID 和 NAME向后兼容旧代码
/// [onSelectedWithIndex]: 可选的新回调,额外返回 indexindex 为在原始 personsData/_all 中的下标,找不到则为 0
static Future<void> show(
BuildContext context, {
required List<Map<String, dynamic>> personsData,
PersonSelectCallback? onSelected,
PersonSelectCallbackWithIndex? onSelectedWithIndex,
}) async {
// 至少传入一个回调(保持对旧调用的兼容)
assert(onSelected != null || onSelectedWithIndex != null,
'请至少传入 onSelected 或 onSelectedWithIndex');
// 转换为模型
final List<Person> _all = personsData.map((e) => Person.fromJson(e)).toList();
List<Person> _filtered = List.from(_all);
String _selectedName = '';
String _selectedId = '';
final TextEditingController _searchController = TextEditingController();
await showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
),
builder: (ctx) {
return StatefulBuilder(
builder: (BuildContext ctx, StateSetter setState) {
// 搜索逻辑
void _onSearch(String v) {
final q = v.toLowerCase().trim();
setState(() {
_filtered = q.isEmpty
? List.from(_all)
: _all.where((p) => p.name.toLowerCase().contains(q)).toList();
});
}
return SafeArea(
child: SizedBox(
height: MediaQuery.of(ctx).size.height * 0.75,
child: Column(
children: [
// 顶部:取消、搜索、确定
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: const Text(
'取消',
style: TextStyle(fontSize: 16),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: SearchBarWidget(
controller: _searchController,
onTextChanged: _onSearch,
isShowSearchButton: false,
onSearch: (keyboard) {},
),
),
),
TextButton(
onPressed: _selectedId.isEmpty
? null
: () {
Navigator.of(ctx).pop();
// 计算 index在原始 _all 列表中的下标)
final idx = _all.indexWhere((p) => p.userId == _selectedId);
final validIndex = idx >= 0 ? idx : 0;
// 优先调用带 index 的回调(新),否则调用旧回调
if (onSelectedWithIndex != null) {
onSelectedWithIndex(_selectedId, _selectedName, validIndex);
} else if (onSelected != null) {
onSelected(_selectedId, _selectedName);
}
},
child: const Text(
'确定',
style: TextStyle(color: Colors.green, fontSize: 16),
),
),
],
),
),
const Divider(height: 1),
// 列表
Expanded(
child: ListView.separated(
itemCount: _filtered.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, index) {
final person = _filtered[index];
final selected = person.userId == _selectedId;
return ListTile(
titleAlignment: ListTileTitleAlignment.center,
title: Text(person.name),
trailing: selected ? const Icon(Icons.check, color: Colors.green) : null,
onTap: () => setState(() {
_selectedId = person.userId;
_selectedName = person.name;
}),
);
},
),
),
],
),
),
);
},
);
},
);
}
}