checkbox.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import 'package:flutter/material.dart';
  2. /// 多选框组
  3. class VCheckBoxGroup<T, TValue> extends StatefulWidget {
  4. final List<T> source;
  5. final List<TValue>? values;
  6. final String Function(T data) labelGetter;
  7. final TValue Function(T data) valueGetter;
  8. final ValueChanged<List<TValue>>? onChanged;
  9. final double? itemSpace;
  10. const VCheckBoxGroup({
  11. super.key,
  12. required this.source,
  13. this.values,
  14. required this.labelGetter,
  15. required this.valueGetter,
  16. this.onChanged,
  17. this.itemSpace,
  18. });
  19. @override
  20. State<StatefulWidget> createState() => _VCheckBoxGroupState<T, TValue>();
  21. }
  22. class _VCheckBoxGroupState<T, TValue> extends State<VCheckBoxGroup<T, TValue>> {
  23. late List<TValue> _checkedValues;
  24. @override
  25. void initState() {
  26. _checkedValues = widget.values ?? [];
  27. super.initState();
  28. }
  29. @override
  30. Widget build(BuildContext context) {
  31. final children = <Widget>[];
  32. final length = widget.source.length;
  33. for (var i = 0; i < length; i++) {
  34. final e = widget.source[i];
  35. children.add(
  36. VCheckBox(
  37. label: widget.labelGetter(e),
  38. isChecked: false,
  39. onChanged: (value) {
  40. _onItemChanged(e, value);
  41. },
  42. ),
  43. );
  44. if (i < length - 1) {
  45. children.add(SizedBox(width: widget.itemSpace));
  46. }
  47. }
  48. // return Flow(
  49. // delegate: FlowlayoutDelegate(),
  50. // children: children,
  51. // );
  52. return Row(
  53. mainAxisSize: MainAxisSize.min,
  54. children: children,
  55. );
  56. }
  57. void _onItemChanged(T data, bool isChecked) {
  58. final value = widget.valueGetter(data);
  59. if (isChecked) {
  60. if (_checkedValues.contains(value) == false) {
  61. _checkedValues.add(value);
  62. }
  63. } else {
  64. _checkedValues.remove(value);
  65. }
  66. widget.onChanged?.call(_checkedValues);
  67. }
  68. }
  69. /// 多选框
  70. class VCheckBox extends StatefulWidget {
  71. /// 文本
  72. final String label;
  73. /// 是否默认选中
  74. final bool? isChecked;
  75. /// 选中状态变更
  76. final ValueChanged<bool>? onChanged;
  77. const VCheckBox({
  78. super.key,
  79. required this.label,
  80. this.isChecked,
  81. this.onChanged,
  82. });
  83. @override
  84. State<StatefulWidget> createState() => _VCheckBoxState();
  85. }
  86. class _VCheckBoxState extends State<VCheckBox> {
  87. bool _isChecked = false;
  88. @override
  89. void initState() {
  90. if (widget.isChecked != null) {
  91. _isChecked = widget.isChecked!;
  92. }
  93. super.initState();
  94. }
  95. @override
  96. Widget build(BuildContext context) {
  97. return Material(
  98. child: Ink(
  99. child: InkWell(
  100. onTap: () {
  101. setState(() {
  102. _isChecked = !_isChecked;
  103. widget.onChanged?.call(_isChecked);
  104. });
  105. },
  106. child: Container(
  107. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  108. alignment: Alignment.center,
  109. child: Row(
  110. mainAxisSize: MainAxisSize.min,
  111. crossAxisAlignment: CrossAxisAlignment.center,
  112. children: [
  113. _CheckStatusTag(isChecked: _isChecked),
  114. const SizedBox(width: 6),
  115. Text(
  116. widget.label,
  117. style: const TextStyle(color: Colors.black, fontSize: 16),
  118. ),
  119. ],
  120. ),
  121. ),
  122. ),
  123. ),
  124. );
  125. }
  126. }
  127. class _CheckStatusTag extends StatelessWidget {
  128. final bool isChecked;
  129. const _CheckStatusTag({super.key, required this.isChecked});
  130. @override
  131. Widget build(BuildContext context) {
  132. final color = Theme.of(context).primaryColor;
  133. return Container(
  134. alignment: Alignment.center,
  135. width: 18,
  136. height: 18,
  137. decoration: BoxDecoration(
  138. color: isChecked ? color : Colors.white,
  139. border: Border.all(width: 1, color: color),
  140. borderRadius: BorderRadius.zero,
  141. // boxShadow: [],
  142. ),
  143. child: isChecked
  144. ? const Icon(Icons.check, size: 16, color: Colors.white)
  145. : null,
  146. );
  147. }
  148. }