import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:lszlgl/config/colors.dart'; import 'package:lszlgl/ext/value_notifier_ext.dart'; import '../router/my_navigator.dart'; import 'menu_dialog.dart'; class CardItemWidget extends StatelessWidget { /// 标题文本 final String title; /// 左侧icon final Widget? leading; /// 右侧icon final Widget? trailing; /// 右侧文本 final String? rightText; final String? hint; /// 自定义右侧组件 final Widget? rightChild; /// 右侧组件对齐方式 final AlignmentGeometry rightAlign; final bool showTopLine; final bool bottomLine; /// 点击事件 final VoidCallback? onTap; final Color? backgroundColor; // 左右两边 填充比例 final int leftRatio; final int rightRatio; const CardItemWidget( this.title, { Key? key, this.leading, this.trailing, this.rightText, this.hint, this.rightChild, this.onTap, this.rightAlign = Alignment.centerRight, this.showTopLine = false, this.bottomLine = false, this.backgroundColor = Colors.white, this.leftRatio = 1, this.rightRatio = 1, }) : super(key: key); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, behavior: HitTestBehavior.opaque, child: Column( mainAxisSize: MainAxisSize.min, children: [ showTopLine ? const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1) : const SizedBox.shrink(), Container( constraints: const BoxConstraints(minHeight: 48), padding: const EdgeInsets.all(12), color: backgroundColor, child: Row( children: [ buildLeading(), buildTitle(), const SizedBox(width: 4), buildRight(), buildTrailing(), ], ), ), bottomLine ? const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1) : const SizedBox.shrink(), ], ), ); } Widget buildLeading() { if (leading == null) return const SizedBox.shrink(); return Padding( padding: const EdgeInsets.only(right: 8), child: leading, ); } Widget buildTrailing() { if (trailing == null) return const SizedBox.shrink(); return Padding( padding: const EdgeInsets.only(left: 8), child: trailing, ); } Widget buildTitle() { return Expanded( flex: leftRatio, child: Text( title, style: const TextStyle( color: Color(0xFF515151), fontSize: 14, fontWeight: FontWeight.w300, ), ), ); } Widget buildRight() { Widget child; if (rightChild != null) { child = rightChild!; } else { child = Text( (rightText?.isNotEmpty ?? false) ? rightText! : hint ?? '', textAlign: TextAlign.right, style: TextStyle( color: (rightText?.isNotEmpty ?? false) ? const Color(0xFF01B2C8) : MyColor.c_666666, fontSize: 14, fontWeight: FontWeight.w500, ), ); } return Expanded( flex: rightRatio, child: Container( alignment: rightAlign, child: child, ), ); } } //ignore: must_be_immutable class CardItemMenuWidget extends StatelessWidget { /// 标题文本 final String title; /// 左侧icon final Widget? leading; final String? hint; /// 右侧组件对齐方式 final AlignmentGeometry rightAlign; final bool showTopLine; final bool bottomLine; final Color? backgroundColor; final Color? menuColor; final int leftRatio; final int rightRatio; final ValueNotifier> listNotifier; final ValueNotifier selNotifier; final void Function(ValueNotifier, CardMenuData)? onSelectTap; late MenuController _ctrl; CardItemMenuWidget( this.title, this.listNotifier, this.selNotifier, { Key? key, this.leading, this.hint, this.rightAlign = Alignment.centerRight, this.showTopLine = false, this.bottomLine = false, this.backgroundColor = Colors.white, this.menuColor = Colors.white, this.onSelectTap, this.leftRatio = 1, this.rightRatio = 1, }) : super(key: key); void clickMenu() { if (listNotifier.value.isEmpty) { MyNavigator.showToast('没有选项数据'); return; } if (_ctrl.isOpen) { _ctrl.close(); } else { _ctrl.open(); } } @override Widget build(BuildContext context) { return CardItemWidget( title, leading: leading, rightAlign: rightAlign, showTopLine: showTopLine, bottomLine: bottomLine, backgroundColor: backgroundColor, rightChild: buildMenu(), leftRatio: leftRatio, rightRatio: rightRatio, ); } Widget buildMenu() { return listNotifier.builder( (list) => selNotifier.builder( (selData) { return MenuAnchor( style: MenuStyle( padding: const WidgetStatePropertyAll(EdgeInsets.zero), alignment: Alignment.bottomRight, surfaceTintColor: WidgetStatePropertyAll(menuColor), elevation: const WidgetStatePropertyAll(16), shape: const WidgetStatePropertyAll(RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(16)), ))), builder: (_, ctrl, __) => buildMenuLabel(ctrl, selData), menuChildren: List.generate(list.length, (index) { return buildMenuItem(list[index], selData); }).toList(), ); }, ), ); } /// 菜单栏 Widget buildMenuLabel(MenuController ctrl, CardMenuData? selData) { _ctrl = ctrl; return GestureDetector( behavior: HitTestBehavior.opaque, onTap: clickMenu, child: Row( mainAxisSize: MainAxisSize.min, children: [ Expanded( child: Text( selData?.name != null ? selData!.name! : hint ?? '', textAlign: TextAlign.right, style: TextStyle( color: selData?.name != null ? const Color(0xFF01B2C8) : MyColor.c_666666, fontSize: 14, fontWeight: FontWeight.w500, ), ), ), selData == null ? const Padding( padding: EdgeInsets.only(left: 4), child: Icon( Icons.keyboard_arrow_down, size: 24, color: Color(0xFF01B2C8), ), ) : const SizedBox.shrink(), ], ), ); } Widget buildMenuItem(CardMenuData item, CardMenuData? selData) { return MenuItemButton( onPressed: () { if (item.value == selData?.value) return; selNotifier.value = item; onSelectTap?.call(selNotifier, item); }, child: Container( constraints: const BoxConstraints(minWidth: 120), alignment: Alignment.center, child: Text( item.name ?? '', style: TextStyle( color: item.value == selData?.value ? const Color(0xFF01B2C8) : MyColor.c_666666), ), ), ); } } class CardMenuData { String? name; dynamic value; bool select = false; CardMenuData(this.name, this.value); @override bool operator ==(Object other) { if (other is! CardMenuData) return false; return name == other.name && value == other.value; } } class CardWidgets { CardWidgets._(); static Widget buildEdit( bool isDetail, String title, String? value, { String? hint = '点击填写', TextInputType? inputType, ValueChanged? onChanged, List? formatters, Color? backgroundColor = Colors.white, bool bottomLine = true, bool obscureText = false, TextEditingController? ctrl, int? maxLength, String? errorText, int leftRatio = 1, int rightRatio = 1, }) { if (isDetail) { return CardItemWidget( title, rightText: value, bottomLine: bottomLine, backgroundColor: backgroundColor, leftRatio: leftRatio, rightRatio: rightRatio, ); } return CardItemWidget( title, rightChild: TextFormField( controller: ctrl, initialValue: ctrl == null ? value : null, keyboardType: inputType, textAlign: TextAlign.right, decoration: InputDecoration( hintText: hint, hintStyle: const TextStyle(color: MyColor.c_666666, fontSize: 14), border: InputBorder.none, contentPadding: EdgeInsets.zero, isDense: true, errorText: errorText, ), style: const TextStyle(color: Color(0xFF01B2C8), fontSize: 14, fontWeight: FontWeight.w500), textInputAction: TextInputAction.next, inputFormatters: formatters, onChanged: onChanged, obscureText: obscureText, maxLength: maxLength, ), backgroundColor: backgroundColor, bottomLine: bottomLine, leftRatio: leftRatio, rightRatio: rightRatio, ); } static Widget buildDate( bool isDetail, String title, ValueNotifier notifier, { String? Function(DateTime?)? onResult, DateTime? firstDate, DateTime? lastDate, }) { return notifier.builder((v) { if (isDetail) { return CardItemWidget(title, rightText: v, bottomLine: true); } return CardItemWidget( title, rightText: v, trailing: v == null ? const Icon( Icons.keyboard_arrow_down, size: 24, color: Color(0xFF01B2C8), ) : const SizedBox.shrink(), bottomLine: true, onTap: () async { var date = await showDatePicker( context: MyNavigator.navigator.currentState!.context, firstDate: firstDate ?? DateTime(2020), lastDate: lastDate ?? DateTime.now(), ); if (date != null && onResult != null) { notifier.value = onResult.call(date); } }, ); }); } static Widget buildMenuDialog( bool isDetail, String title, String? value, List? list, void Function(List)? onSelectTap, { bool multiple = false, String? hint, Widget? trailing, int? selectCountMax, bool bottomLine = true, bool showTopLine = false, }) { return CardItemWidget( title, rightText: value, bottomLine: bottomLine, showTopLine: showTopLine, hint: hint, trailing: isDetail || value != null ? null : trailing ?? const Icon( Icons.keyboard_arrow_down, size: 24, color: Color(0xFF01B2C8), ), onTap: isDetail ? null : () async { List? selectList = await showMenuDialog( list, title: title, multiple: multiple, selectCountMax: selectCountMax, ); if (selectList == null) return; onSelectTap?.call(selectList); }, ); } static Widget buildMenu( bool isDetail, String title, ValueNotifier> listNotifier, ValueNotifier selNotifier, void Function(ValueNotifier, CardMenuData)? onSelectTap, { String? hint, bool showTopLine = false, bool bottomLine = true, int leftRatio = 1, int rightRatio = 1, }) { if (isDetail) { return selNotifier.builder( (v) => CardItemWidget( title, rightText: v?.name, bottomLine: bottomLine, hint: hint, showTopLine: showTopLine, leftRatio: leftRatio, rightRatio: rightRatio, ), ); } return CardItemMenuWidget( title, listNotifier, selNotifier, onSelectTap: onSelectTap, bottomLine: bottomLine, hint: hint, showTopLine: showTopLine, leftRatio: leftRatio, rightRatio: rightRatio, ); } }