123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- 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<List<CardMenuData>> listNotifier;
- final ValueNotifier<CardMenuData?> selNotifier;
- final void Function(ValueNotifier<CardMenuData?>, 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<String>? onChanged,
- List<TextInputFormatter>? 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<String?> notifier, {
- String? Function(DateTime?)? onResult,
- DateTime? firstDate,
- DateTime? lastDate,
- String? warmString, // 警告语,日期不可选择时 禁止点击并提示
- }) {
- 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 {
- if(warmString != null){
- MyNavigator.showToast(warmString);
- return ;
- }
- 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<CardMenuData>? list,
- void Function(List<CardMenuData>)? 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<CardMenuData>? 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<List<CardMenuData>> listNotifier,
- ValueNotifier<CardMenuData?> selNotifier,
- void Function(ValueNotifier<CardMenuData?>, 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,
- );
- }
- }
|