card_item.dart 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart';
  3. import 'package:lszlgl/config/colors.dart';
  4. import 'package:lszlgl/ext/value_notifier_ext.dart';
  5. import '../config/pics.dart';
  6. import '../router/my_navigator.dart';
  7. class CardItemWidget extends StatelessWidget {
  8. /// 标题文本
  9. final String title;
  10. /// 左侧icon
  11. final Widget? leading;
  12. /// 右侧icon
  13. final Widget? trailing;
  14. /// 右侧文本
  15. final String? rightText;
  16. final String? hint;
  17. /// 自定义右侧组件
  18. final Widget? rightChild;
  19. /// 右侧组件对齐方式
  20. final AlignmentGeometry rightAlign;
  21. final bool showTopLine;
  22. final bool bottomLine;
  23. /// 点击事件
  24. final VoidCallback? onTap;
  25. final Color? backgroundColor;
  26. const CardItemWidget(
  27. this.title, {
  28. Key? key,
  29. this.leading,
  30. this.trailing,
  31. this.rightText,
  32. this.hint,
  33. this.rightChild,
  34. this.onTap,
  35. this.rightAlign = Alignment.centerRight,
  36. this.showTopLine = false,
  37. this.bottomLine = false,
  38. this.backgroundColor = Colors.white,
  39. }) : super(key: key);
  40. @override
  41. Widget build(BuildContext context) {
  42. return GestureDetector(
  43. onTap: onTap,
  44. behavior: HitTestBehavior.opaque,
  45. child: Column(
  46. mainAxisSize: MainAxisSize.min,
  47. children: [
  48. showTopLine ? const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1) : const SizedBox.shrink(),
  49. Container(
  50. constraints: const BoxConstraints(minHeight: 48),
  51. padding: const EdgeInsets.all(12),
  52. color: backgroundColor,
  53. child: Row(
  54. children: [
  55. buildLeading(),
  56. buildTitle(),
  57. const SizedBox(width: 4),
  58. buildRight(),
  59. buildTrailing(),
  60. ],
  61. ),
  62. ),
  63. bottomLine ? const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1) : const SizedBox.shrink(),
  64. ],
  65. ),
  66. );
  67. }
  68. Widget buildLeading() {
  69. if (leading == null) return const SizedBox.shrink();
  70. return Padding(
  71. padding: const EdgeInsets.only(right: 8),
  72. child: leading,
  73. );
  74. }
  75. Widget buildTrailing() {
  76. if (trailing == null) return const SizedBox.shrink();
  77. return Padding(
  78. padding: const EdgeInsets.only(left: 8),
  79. child: trailing,
  80. );
  81. }
  82. Widget buildTitle() {
  83. return Expanded(
  84. child: Text(
  85. title,
  86. style: const TextStyle(
  87. color: Color(0xFF333333),
  88. fontSize: 14,
  89. fontWeight: FontWeight.w500,
  90. ),
  91. ),
  92. );
  93. }
  94. Widget buildRight() {
  95. Widget child;
  96. if (rightChild != null) {
  97. child = rightChild!;
  98. } else {
  99. child = Text(
  100. (rightText?.isNotEmpty ?? false) ? rightText! : hint ?? '',
  101. textAlign: TextAlign.right,
  102. style: TextStyle(
  103. color: (rightText?.isNotEmpty ?? false) ? const Color(0xFF01B2C8) : MyColor.c_666666,
  104. fontSize: 14,
  105. fontWeight: FontWeight.w500,
  106. ),
  107. );
  108. }
  109. return Expanded(
  110. child: Container(
  111. alignment: rightAlign,
  112. child: child,
  113. ),
  114. );
  115. }
  116. }
  117. class CardItemMenuWidget extends StatelessWidget {
  118. /// 标题文本
  119. final String title;
  120. /// 左侧icon
  121. final Widget? leading;
  122. final String? hint;
  123. /// 右侧组件对齐方式
  124. final AlignmentGeometry rightAlign;
  125. final bool showTopLine;
  126. final bool bottomLine;
  127. final Color? backgroundColor;
  128. final Color? menuColor;
  129. final ValueNotifier<List<CardMenuData>> listNotifier;
  130. final ValueNotifier<CardMenuData?> selNotifier;
  131. final void Function(ValueNotifier<CardMenuData?>, CardMenuData)? onSelectTap;
  132. const CardItemMenuWidget(
  133. this.title,
  134. this.listNotifier,
  135. this.selNotifier, {
  136. Key? key,
  137. this.leading,
  138. this.hint,
  139. this.rightAlign = Alignment.centerRight,
  140. this.showTopLine = false,
  141. this.bottomLine = false,
  142. this.backgroundColor = Colors.white,
  143. this.menuColor = Colors.white,
  144. this.onSelectTap,
  145. }) : super(key: key);
  146. @override
  147. Widget build(BuildContext context) {
  148. return CardItemWidget(
  149. title,
  150. leading: leading,
  151. rightAlign: rightAlign,
  152. showTopLine: showTopLine,
  153. bottomLine: bottomLine,
  154. backgroundColor: backgroundColor,
  155. rightChild: buildMenu(),
  156. );
  157. }
  158. Widget buildMenu() {
  159. return listNotifier.builder(
  160. (list) => selNotifier.builder(
  161. (selData) {
  162. return MenuAnchor(
  163. style: MenuStyle(
  164. padding: const MaterialStatePropertyAll(EdgeInsets.zero),
  165. alignment: Alignment.bottomRight,
  166. surfaceTintColor: MaterialStatePropertyAll(menuColor),
  167. elevation: const MaterialStatePropertyAll(16),
  168. shape: const MaterialStatePropertyAll(RoundedRectangleBorder(
  169. borderRadius: BorderRadius.all(Radius.circular(16)),
  170. ))),
  171. builder: (_, ctrl, __) => buildMenuLabel(ctrl, selData),
  172. menuChildren: List.generate(list.length, (index) {
  173. return buildMenuItem(list[index], selData);
  174. }).toList(),
  175. );
  176. },
  177. ),
  178. );
  179. }
  180. /// 菜单栏
  181. Widget buildMenuLabel(MenuController ctrl, CardMenuData? selData) {
  182. return GestureDetector(
  183. behavior: HitTestBehavior.opaque,
  184. onTap: () {
  185. if (listNotifier.value.isEmpty) {
  186. MyNavigator.showToast('没有选项数据');
  187. return;
  188. }
  189. if (ctrl.isOpen) {
  190. ctrl.close();
  191. } else {
  192. ctrl.open();
  193. }
  194. },
  195. child: Row(
  196. mainAxisSize: MainAxisSize.min,
  197. children: [
  198. Expanded(
  199. child: Text(
  200. selData?.name ?? '',
  201. textAlign: TextAlign.right,
  202. style: const TextStyle(
  203. color: Color(0xFF01B2C8),
  204. fontSize: 14,
  205. fontWeight: FontWeight.w500,
  206. ),
  207. ),
  208. ),
  209. const SizedBox(width: 8),
  210. Image.asset(imgItemArrowDown, width: 20),
  211. ],
  212. ),
  213. );
  214. }
  215. Widget buildMenuItem(CardMenuData item, CardMenuData? selData) {
  216. return MenuItemButton(
  217. onPressed: () {
  218. if (item.value == selData?.value) return;
  219. selNotifier.value = item;
  220. onSelectTap?.call(selNotifier, item);
  221. },
  222. child: Container(
  223. constraints: const BoxConstraints(minWidth: 120),
  224. alignment: Alignment.center,
  225. child: Text(
  226. item.name ?? '',
  227. style: TextStyle(color: item.value == selData?.value ? const Color(0xFF01B2C8) : MyColor.c_666666),
  228. ),
  229. ),
  230. );
  231. }
  232. }
  233. class CardMenuData {
  234. String? name;
  235. dynamic value;
  236. CardMenuData(this.name, this.value);
  237. }
  238. class CardWidgets {
  239. CardWidgets._();
  240. static Widget buildEdit(
  241. bool isDetail,
  242. String title,
  243. String? value, {
  244. String? hint = '点击填写',
  245. TextInputType? inputType,
  246. ValueChanged<String>? onChanged,
  247. List<TextInputFormatter>? formatters,
  248. Color? backgroundColor = Colors.white,
  249. }) {
  250. if (isDetail) {
  251. return CardItemWidget(
  252. title,
  253. rightText: value,
  254. bottomLine: true,
  255. backgroundColor: backgroundColor,
  256. );
  257. }
  258. return CardItemWidget(
  259. title,
  260. rightChild: TextField(
  261. controller: TextEditingController(text: value),
  262. keyboardType: inputType,
  263. textAlign: TextAlign.right,
  264. decoration: InputDecoration(
  265. hintText: hint,
  266. hintStyle: const TextStyle(color: MyColor.c_666666, fontSize: 14),
  267. border: InputBorder.none,
  268. contentPadding: EdgeInsets.zero,
  269. isDense: true,
  270. ),
  271. style: const TextStyle(color: Color(0xFF01B2C8), fontSize: 14, fontWeight: FontWeight.w500),
  272. textInputAction: TextInputAction.next,
  273. inputFormatters: formatters,
  274. onChanged: onChanged,
  275. ),
  276. backgroundColor: backgroundColor,
  277. bottomLine: true,
  278. );
  279. }
  280. static Widget buildDate(
  281. bool isDetail,
  282. String title,
  283. ValueNotifier<String?> notifier, {
  284. String? Function(DateTime?)? onResult,
  285. DateTime? firstDate,
  286. DateTime? lastDate,
  287. }) {
  288. return notifier.builder((v) {
  289. if (isDetail) {
  290. return CardItemWidget(title, rightText: v, bottomLine: true);
  291. }
  292. return CardItemWidget(
  293. title,
  294. rightText: v,
  295. trailing: Image.asset(imgItemArrowDown, width: 20),
  296. bottomLine: true,
  297. onTap: () async {
  298. var date = await showDatePicker(
  299. context: MyNavigator.navigator.currentState!.context,
  300. firstDate: firstDate ?? DateTime(2020),
  301. lastDate: lastDate ?? DateTime.now(),
  302. );
  303. if (onResult != null) {
  304. notifier.value = onResult.call(date);
  305. }
  306. },
  307. );
  308. });
  309. }
  310. static Widget buildMenu(
  311. bool isDetail,
  312. String title,
  313. ValueNotifier<List<CardMenuData>> listNotifier,
  314. ValueNotifier<CardMenuData?> selNotifier,
  315. void Function(ValueNotifier<CardMenuData?>, CardMenuData)? onSelectTap,
  316. ) {
  317. if (isDetail) {
  318. return selNotifier.builder(
  319. (v) => CardItemWidget(
  320. title,
  321. rightText: v?.name,
  322. bottomLine: true,
  323. ),
  324. );
  325. }
  326. return CardItemMenuWidget(
  327. title,
  328. listNotifier,
  329. selNotifier,
  330. onSelectTap: onSelectTap,
  331. bottomLine: true,
  332. );
  333. }
  334. }