vb_select.dart 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import 'package:flutter/material.dart';
  2. /// 数据转换函数签名
  3. /// 将原始数据转换为SelectOption类型的函数
  4. typedef SelectOptionConverter<T> = SelectOption Function(T data);
  5. /// 选择器选项数据模型
  6. class SelectOption {
  7. final String label;
  8. final String value;
  9. final dynamic extra;
  10. SelectOption({required this.label, required this.value, this.extra});
  11. }
  12. /// 通用选择器组件
  13. /// 支持静态数据源和动态API数据源
  14. class VberSelect<T> extends StatefulWidget {
  15. /// 静态数据列表
  16. final List<T>? items;
  17. /// API获取数据的方法
  18. final Future<List<T>> Function()? fetchData;
  19. /// 数据转换方法,将原始数据转换为SelectOption
  20. final SelectOptionConverter<T> converter;
  21. /// 当前选中的值
  22. final String? value;
  23. /// 值改变回调
  24. final Function(String?)? onChanged;
  25. /// 提示文字
  26. final String? hint;
  27. /// 是否启用
  28. final bool enabled;
  29. /// 是否显示"全部"选项
  30. final bool showAll;
  31. /// 是否隐藏下划线
  32. final bool hideUnderline;
  33. /// 是否显示清空按钮
  34. final bool showClearButton;
  35. const VberSelect({
  36. super.key,
  37. this.items,
  38. this.fetchData,
  39. required this.converter,
  40. this.value,
  41. this.onChanged,
  42. this.hint,
  43. this.enabled = true,
  44. this.showAll = false,
  45. this.hideUnderline = false,
  46. this.showClearButton = false,
  47. });
  48. @override
  49. State<VberSelect<T>> createState() => _VberSelectState<T>();
  50. }
  51. class _VberSelectState<T> extends State<VberSelect<T>> {
  52. List<T>? _items;
  53. bool _loading = false;
  54. String? _selectedValue;
  55. @override
  56. void initState() {
  57. super.initState();
  58. _selectedValue = widget.value;
  59. // 使用WidgetsBinding.instance.addPostFrameCallback确保在widget构建完成后再加载数据
  60. WidgetsBinding.instance.addPostFrameCallback((_) {
  61. _loadData();
  62. });
  63. }
  64. @override
  65. void didUpdateWidget(covariant VberSelect<T> oldWidget) {
  66. super.didUpdateWidget(oldWidget);
  67. // 当外部传入的value发生变化时,同步更新内部状态
  68. if (oldWidget.value != widget.value) {
  69. setState(() {
  70. _selectedValue = widget.value;
  71. });
  72. }
  73. }
  74. Future<void> _loadData() async {
  75. // 如果提供了fetchData方法,则优先使用它获取数据
  76. if (widget.fetchData != null) {
  77. // 使用Future.microtask确保在下一个微任务中加载数据,避免在构建过程中修改状态
  78. Future.microtask(() async {
  79. setState(() {
  80. _loading = true;
  81. });
  82. try {
  83. final data = await widget.fetchData!();
  84. setState(() {
  85. _items = data;
  86. _loading = false;
  87. });
  88. } catch (e) {
  89. setState(() {
  90. _loading = false;
  91. });
  92. }
  93. });
  94. } else {
  95. // 否则使用传入的静态数据
  96. setState(() {
  97. _items = widget.items;
  98. });
  99. }
  100. }
  101. @override
  102. Widget build(BuildContext context) {
  103. if (_loading) {
  104. Widget dropdownWidget = DropdownButton<String>(
  105. items: [],
  106. hint: SizedBox(
  107. width: 16,
  108. height: 16,
  109. child: CircularProgressIndicator(strokeWidth: 2),
  110. ),
  111. onChanged: (String? value) {},
  112. );
  113. Widget selectWidget = widget.hideUnderline
  114. ? DropdownButtonHideUnderline(child: dropdownWidget)
  115. : dropdownWidget;
  116. if (widget.showClearButton) {
  117. return Row(
  118. children: [
  119. Expanded(child: selectWidget),
  120. IconButton(
  121. icon: const Icon(Icons.clear, size: 20),
  122. onPressed: widget.enabled
  123. ? () {
  124. setState(() {
  125. _selectedValue = null;
  126. });
  127. widget.onChanged?.call(null);
  128. }
  129. : null,
  130. ),
  131. ],
  132. );
  133. }
  134. return selectWidget;
  135. }
  136. // 构建最终的下拉选项列表
  137. List<DropdownMenuItem<String>> dropdownItems = [];
  138. // 如果showAll为true,添加"全部"选项
  139. if (widget.showAll) {
  140. dropdownItems.add(DropdownMenuItem<String>(value: '', child: Text('全部')));
  141. }
  142. // 只有当_items不为null且不为空时才添加实际选项
  143. if (_items != null && _items!.isNotEmpty) {
  144. final options = _items!.map((item) => widget.converter(item)).toList();
  145. // 添加转换后的选项
  146. dropdownItems.addAll(
  147. options.map((option) {
  148. return DropdownMenuItem<String>(
  149. value: option.value,
  150. child: Text(option.label),
  151. );
  152. }).toList(),
  153. );
  154. }
  155. Widget dropdownWidget = DropdownButton<String>(
  156. value: _selectedValue,
  157. hint: Text(widget.hint ?? '请选择'),
  158. items: dropdownItems,
  159. onChanged: widget.enabled
  160. ? (String? newValue) {
  161. setState(() {
  162. _selectedValue = newValue;
  163. });
  164. widget.onChanged?.call(newValue);
  165. }
  166. : null,
  167. isExpanded: true,
  168. );
  169. Widget selectWidget = widget.hideUnderline
  170. ? DropdownButtonHideUnderline(child: dropdownWidget)
  171. : dropdownWidget;
  172. if (widget.showClearButton) {
  173. return Row(
  174. children: [
  175. Expanded(child: selectWidget),
  176. IconButton(
  177. icon: const Icon(Icons.clear, size: 20),
  178. onPressed: widget.enabled
  179. ? () {
  180. setState(() {
  181. _selectedValue = null;
  182. });
  183. widget.onChanged?.call(null);
  184. }
  185. : null,
  186. ),
  187. ],
  188. );
  189. }
  190. return selectWidget;
  191. }
  192. }