vb_select.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. const VberSelect({
  32. super.key,
  33. this.items,
  34. this.fetchData,
  35. required this.converter,
  36. this.value,
  37. this.onChanged,
  38. this.hint,
  39. this.enabled = true,
  40. this.showAll = false,
  41. });
  42. @override
  43. State<VberSelect<T>> createState() => _VberSelectState<T>();
  44. }
  45. class _VberSelectState<T> extends State<VberSelect<T>> {
  46. List<T>? _items;
  47. bool _loading = false;
  48. String? _selectedValue;
  49. @override
  50. void initState() {
  51. super.initState();
  52. _selectedValue = widget.value;
  53. // 使用WidgetsBinding.instance.addPostFrameCallback确保在widget构建完成后再加载数据
  54. WidgetsBinding.instance.addPostFrameCallback((_) {
  55. _loadData();
  56. });
  57. }
  58. @override
  59. void didUpdateWidget(covariant VberSelect<T> oldWidget) {
  60. super.didUpdateWidget(oldWidget);
  61. // 当外部传入的value发生变化时,同步更新内部状态
  62. if (oldWidget.value != widget.value) {
  63. setState(() {
  64. _selectedValue = widget.value;
  65. });
  66. }
  67. }
  68. Future<void> _loadData() async {
  69. // 如果提供了fetchData方法,则优先使用它获取数据
  70. if (widget.fetchData != null) {
  71. // 使用Future.microtask确保在下一个微任务中加载数据,避免在构建过程中修改状态
  72. Future.microtask(() async {
  73. setState(() {
  74. _loading = true;
  75. });
  76. try {
  77. final data = await widget.fetchData!();
  78. setState(() {
  79. _items = data;
  80. _loading = false;
  81. });
  82. } catch (e) {
  83. setState(() {
  84. _loading = false;
  85. });
  86. }
  87. });
  88. } else {
  89. // 否则使用传入的静态数据
  90. setState(() {
  91. _items = widget.items;
  92. });
  93. }
  94. }
  95. @override
  96. Widget build(BuildContext context) {
  97. if (_loading) {
  98. return DropdownButton<String>(
  99. items: [],
  100. hint: SizedBox(
  101. width: 16,
  102. height: 16,
  103. child: CircularProgressIndicator(strokeWidth: 2),
  104. ),
  105. onChanged: (String? value) {},
  106. );
  107. }
  108. if (_items == null || _items!.isEmpty) {
  109. List<DropdownMenuItem<String>> dropdownItems = [];
  110. // 如果showAll为true,添加"全部"选项
  111. if (widget.showAll) {
  112. dropdownItems.add(
  113. DropdownMenuItem<String>(
  114. value: '',
  115. child: Text('全部'),
  116. ),
  117. );
  118. }
  119. return DropdownButton<String>(
  120. value: _selectedValue,
  121. hint: Text(widget.hint ?? '请选择'),
  122. items: dropdownItems,
  123. onChanged: widget.enabled ? widget.onChanged : null,
  124. );
  125. }
  126. final options = _items!.map((item) => widget.converter(item)).toList();
  127. // 构建最终的下拉选项列表
  128. List<DropdownMenuItem<String>> dropdownItems = [];
  129. // 如果showAll为true,添加"全部"选项
  130. if (widget.showAll) {
  131. dropdownItems.add(
  132. DropdownMenuItem<String>(
  133. value: '',
  134. child: Text('全部'),
  135. ),
  136. );
  137. }
  138. // 添加转换后的选项
  139. dropdownItems.addAll(options.map((option) {
  140. return DropdownMenuItem<String>(
  141. value: option.value,
  142. child: Text(option.label),
  143. );
  144. }).toList());
  145. return DropdownButton<String>(
  146. value: _selectedValue,
  147. hint: Text(widget.hint ?? '请选择'),
  148. items: dropdownItems,
  149. onChanged: widget.enabled
  150. ? (String? newValue) {
  151. setState(() {
  152. _selectedValue = newValue;
  153. });
  154. widget.onChanged?.call(newValue);
  155. }
  156. : null,
  157. );
  158. }
  159. }