568 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			568 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Dart
		
	
	
| import 'package:flutter/material.dart';
 | ||
| import 'package:qhd_prevention/customWidget/toast_util.dart';
 | ||
| 
 | ||
| import '../http/ApiService.dart';
 | ||
| import '../tools/tools.dart';
 | ||
| 
 | ||
| /// 自定义组件
 | ||
| class ListItemFactory {
 | ||
|   /// 类型1:横向spaceBetween布局两个文本加按钮
 | ||
|   static Widget createRowSpaceBetweenItem({
 | ||
|     required String leftText,
 | ||
|     required String rightText,
 | ||
|     double verticalPadding = 10,
 | ||
|     double horizontalPadding = 0,
 | ||
|     Color textColor = Colors.black,
 | ||
|     bool isRight = false,
 | ||
|     bool isRequired = false,
 | ||
|   }) {
 | ||
|     return Padding(
 | ||
|       padding: EdgeInsets.symmetric(
 | ||
|         vertical: verticalPadding,
 | ||
|         horizontal: horizontalPadding,
 | ||
|       ),
 | ||
|       child: Row(
 | ||
|         mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | ||
|         mainAxisSize: MainAxisSize.max,
 | ||
|         children: [
 | ||
|           Row(
 | ||
|             mainAxisSize: MainAxisSize.min,
 | ||
|             children: [
 | ||
|               if (isRequired) Text('* ', style: TextStyle(color: Colors.red)),
 | ||
|               Text(
 | ||
|                 leftText,
 | ||
|                 style: TextStyle(
 | ||
|                   fontSize: 15,
 | ||
|                   fontWeight: FontWeight.bold,
 | ||
|                   color: textColor,
 | ||
|                 ),
 | ||
|               ),
 | ||
|             ],
 | ||
|           ),
 | ||
|           if (isRight)
 | ||
|             Row(
 | ||
|               mainAxisSize: MainAxisSize.min,
 | ||
|               children: [
 | ||
|                 Flexible(
 | ||
|                   fit: FlexFit.loose,
 | ||
|                   child: Text(
 | ||
|                     rightText,
 | ||
|                     style: TextStyle(fontSize: 15, color: Colors.grey),
 | ||
|                     maxLines: 1,
 | ||
|                     overflow: TextOverflow.ellipsis,
 | ||
|                   ),
 | ||
|                 ),
 | ||
|                 SizedBox(width: 6),
 | ||
|                 Icon(
 | ||
|                   Icons.arrow_forward_ios_rounded,
 | ||
|                   color: Colors.black45,
 | ||
|                   size: 15,
 | ||
|                 ),
 | ||
|               ],
 | ||
|             )
 | ||
|           else
 | ||
|             Flexible(
 | ||
|               fit: FlexFit.loose,
 | ||
|               child: Text(
 | ||
|                 rightText,
 | ||
|                 style: TextStyle(fontSize: 15, color: Colors.grey),
 | ||
|                 maxLines: 1,
 | ||
|                 overflow: TextOverflow.ellipsis,
 | ||
|                 textAlign: TextAlign.right,
 | ||
|               ),
 | ||
|             ),
 | ||
|         ],
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   // 辅助函数:截断文本
 | ||
|   static String _truncateText(String text, int maxLength) {
 | ||
|     if (text.length <= maxLength) return text;
 | ||
|     return text.substring(0, maxLength) + '...';
 | ||
|   }
 | ||
| 
 | ||
|   ///类型2:上下布局两个文本(自适应高度)
 | ||
|   static Widget createColumnTextItem({
 | ||
|     required String topText,
 | ||
|     required String bottomText,
 | ||
|     double verticalPadding = 15,
 | ||
|     double horizontalPadding = 0,
 | ||
|   }) {
 | ||
|     return Padding(
 | ||
|       padding: EdgeInsets.symmetric(
 | ||
|         vertical: verticalPadding,
 | ||
|         horizontal: horizontalPadding,
 | ||
|       ),
 | ||
|       child: Column(
 | ||
|         crossAxisAlignment: CrossAxisAlignment.start,
 | ||
|         mainAxisSize: MainAxisSize.min,
 | ||
|         children: [
 | ||
|           Text(
 | ||
|             topText,
 | ||
|             style: TextStyle(
 | ||
|               fontSize: 15,
 | ||
|               fontWeight: FontWeight.bold,
 | ||
|               color: Colors.black,
 | ||
|             ),
 | ||
|           ),
 | ||
|           const SizedBox(height: 5),
 | ||
|           Text(
 | ||
|             bottomText,
 | ||
|             style: TextStyle(fontSize: 15, color: Colors.grey),
 | ||
|             softWrap: true,
 | ||
|             maxLines: null, // 允许无限行数
 | ||
|           ),
 | ||
|         ],
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   /// 类型3:文本和图片上下布局
 | ||
|   static Widget createTextImageItem({
 | ||
|     required String text,
 | ||
|     required List<String> imageUrls,
 | ||
|     double imageHeight = 90,
 | ||
|     double verticalPadding = 10,
 | ||
|     double horizontalPadding = 0,
 | ||
|     // 点击图片时回调,index 为被点击图片的下标
 | ||
|     void Function(int index)? onImageTapped,
 | ||
|   }) {
 | ||
|     return Padding(
 | ||
|       padding: EdgeInsets.symmetric(
 | ||
|         vertical: verticalPadding,
 | ||
|         horizontal: horizontalPadding,
 | ||
|       ),
 | ||
|       child: Column(
 | ||
|         crossAxisAlignment: CrossAxisAlignment.start,
 | ||
|         children: [
 | ||
|           Text(
 | ||
|             text,
 | ||
|             style: const TextStyle(
 | ||
|               fontSize: 15,
 | ||
|               fontWeight: FontWeight.bold,
 | ||
|               color: Colors.black,
 | ||
|             ),
 | ||
|           ),
 | ||
|           const SizedBox(height: 10),
 | ||
|           Wrap(
 | ||
|             spacing: 8, // 水平间距
 | ||
|             runSpacing: 8, // 垂直间距
 | ||
|             children: List.generate(imageUrls.length, (i) {
 | ||
|               final url = ApiService.baseImgPath + imageUrls[i];
 | ||
|               Widget img;
 | ||
|               if (url.startsWith('http')) {
 | ||
|                 img = Image.network(
 | ||
|                   url,
 | ||
|                   height: imageHeight,
 | ||
|                   width: imageHeight * 3 / 2,
 | ||
|                   fit: BoxFit.fill,
 | ||
|                 );
 | ||
|               } else {
 | ||
|                 img = Image.asset(
 | ||
|                   url,
 | ||
|                   height: imageHeight,
 | ||
|                   width: imageHeight * 3 / 2,
 | ||
|                   fit: BoxFit.fill,
 | ||
|                 );
 | ||
|               }
 | ||
|               return GestureDetector(
 | ||
|                 onTap: () {
 | ||
|                   if (onImageTapped != null) onImageTapped(i);
 | ||
|                 },
 | ||
|                 child: ClipRRect(
 | ||
|                   borderRadius: BorderRadius.circular(4),
 | ||
|                   child: img,
 | ||
|                 ),
 | ||
|               );
 | ||
|             }),
 | ||
|           ),
 | ||
|         ],
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   /// 类型6:文本和视频上下布局
 | ||
|   static Widget createTextVideoItem({
 | ||
|     required String text,
 | ||
|     required String videoUrl,
 | ||
|     double videoHeight = 90,
 | ||
|     double verticalPadding = 10,
 | ||
|     double horizontalPadding = 0,
 | ||
|     VoidCallback? onVideoTapped,
 | ||
|   }) {
 | ||
|     return Padding(
 | ||
|       padding: EdgeInsets.symmetric(
 | ||
|         vertical: verticalPadding,
 | ||
|         horizontal: horizontalPadding,
 | ||
|       ),
 | ||
|       child: Column(
 | ||
|         crossAxisAlignment: CrossAxisAlignment.start,
 | ||
|         children: [
 | ||
|           Text(
 | ||
|             text,
 | ||
|             style: const TextStyle(
 | ||
|               fontSize: 15,
 | ||
|               fontWeight: FontWeight.bold,
 | ||
|               color: Colors.black,
 | ||
|             ),
 | ||
|           ),
 | ||
|           const SizedBox(height: 10),
 | ||
|           videoUrl.isNotEmpty
 | ||
|               ? GestureDetector(
 | ||
|                 onTap: onVideoTapped,
 | ||
|                 child: Container(
 | ||
|                   height: videoHeight,
 | ||
|                   width: videoHeight * 3 / 2,
 | ||
|                   decoration: BoxDecoration(
 | ||
|                     color: Colors.grey[300],
 | ||
|                     borderRadius: BorderRadius.circular(4),
 | ||
|                   ),
 | ||
|                   child: const Center(
 | ||
|                     child: Icon(
 | ||
|                       Icons.play_circle_outline,
 | ||
|                       size: 40,
 | ||
|                       color: Colors.white,
 | ||
|                     ),
 | ||
|                   ),
 | ||
|                 ),
 | ||
|               )
 | ||
|               : SizedBox(height: 10),
 | ||
|         ],
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   ///类型4:一个文本(自适应高度)
 | ||
|   static Widget createAloneTextItem({
 | ||
|     required String text,
 | ||
|     double verticalPadding = 10,
 | ||
|     double horizontalPadding = 0,
 | ||
|   }) {
 | ||
|     return Padding(
 | ||
|       padding: EdgeInsets.symmetric(
 | ||
|         vertical: verticalPadding,
 | ||
|         horizontal: horizontalPadding,
 | ||
|       ),
 | ||
|       child: Column(
 | ||
|         crossAxisAlignment: CrossAxisAlignment.start,
 | ||
|         mainAxisSize: MainAxisSize.min,
 | ||
|         children: [
 | ||
|           Text(
 | ||
|             text,
 | ||
|             style: TextStyle(fontSize: 15, color: Colors.grey),
 | ||
|             softWrap: true,
 | ||
|             maxLines: null, // 允许无限行数
 | ||
|           ),
 | ||
|         ],
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   ///  YesNo
 | ||
|   static Widget createYesNoSection({
 | ||
|     required String title,
 | ||
|     String yesLabel = '是',
 | ||
|     String noLabel = '否',
 | ||
|     required bool? groupValue,
 | ||
|     required ValueChanged<bool> onChanged,
 | ||
|     double verticalPadding = 15,
 | ||
|     double horizontalPadding = 10,
 | ||
|     bool isEdit = true,
 | ||
|     String text = '',
 | ||
|     bool isRequired = false,
 | ||
|   }) {
 | ||
|     return Padding(
 | ||
|       padding: EdgeInsets.only(
 | ||
|         top: 0,
 | ||
|         right: horizontalPadding,
 | ||
|         left: horizontalPadding,
 | ||
|         bottom: verticalPadding,
 | ||
|       ),
 | ||
|       child: Container(
 | ||
|         padding: EdgeInsets.symmetric(horizontal: 10),
 | ||
|         decoration: BoxDecoration(
 | ||
|           color: Colors.white,
 | ||
|           borderRadius: BorderRadius.circular(5),
 | ||
|         ),
 | ||
|         child: Row(
 | ||
|           children: [
 | ||
|             Expanded(
 | ||
|               child: Row(
 | ||
|                 children: [
 | ||
|                   if (isRequired && isEdit)
 | ||
|                     Text('* ', style: TextStyle(color: Colors.red)),
 | ||
|                   Expanded(
 | ||
|                     child: Text(
 | ||
|                       title,
 | ||
|                       style: TextStyle(
 | ||
|                         fontSize: 15,
 | ||
|                         fontWeight: FontWeight.bold,
 | ||
|                         color: Colors.black,
 | ||
|                       ),
 | ||
|                     ),
 | ||
|                   ),
 | ||
|                 ],
 | ||
|               ),
 | ||
|             ),
 | ||
|             if (isEdit)
 | ||
|               Row(
 | ||
|                 children: [
 | ||
|                   Row(
 | ||
|                     children: [
 | ||
|                       Radio<bool>(
 | ||
|                         activeColor: Colors.blue,
 | ||
|                         value: true,
 | ||
|                         groupValue: groupValue,
 | ||
|                         onChanged: (val) => onChanged(val!),
 | ||
|                       ),
 | ||
|                       Text(yesLabel),
 | ||
|                     ],
 | ||
|                   ),
 | ||
|                   const SizedBox(width: 5),
 | ||
|                   Row(
 | ||
|                     children: [
 | ||
|                       Radio<bool>(
 | ||
|                         activeColor: Colors.blue,
 | ||
|                         value: false,
 | ||
|                         groupValue: groupValue,
 | ||
|                         onChanged: (val) => onChanged(val!),
 | ||
|                       ),
 | ||
|                       Text(noLabel),
 | ||
|                     ],
 | ||
|                   ),
 | ||
|                 ],
 | ||
|               ),
 | ||
|             if (!isEdit) Text(text, style: TextStyle()),
 | ||
|           ],
 | ||
|         ),
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   /// 列表标题头(蓝色标识+文字)
 | ||
|   static Widget createBuildSimpleSection(
 | ||
|     String title, {
 | ||
|     double horPadding = 10,
 | ||
|     Color color = Colors.white,
 | ||
|   }) {
 | ||
|     return Container(
 | ||
|       decoration: BoxDecoration(
 | ||
|         color: color,
 | ||
|         borderRadius: BorderRadius.circular(8),
 | ||
|       ),
 | ||
|       child: Padding(
 | ||
|         padding: EdgeInsets.symmetric(horizontal: horPadding, vertical: 10),
 | ||
|         child: Row(
 | ||
|           children: [
 | ||
|             Container(width: 3, height: 15, color: Colors.blue),
 | ||
|             const SizedBox(width: 8),
 | ||
|             Text(
 | ||
|               title,
 | ||
|               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
 | ||
|             ),
 | ||
|           ],
 | ||
|         ),
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   /// 列表标题头(蓝色圆点+文字)
 | ||
|   static Widget createBlueDotSection(
 | ||
|       String title,
 | ||
|       {double horPadding = 10,
 | ||
|       int color=0xFFf1f1f1}) {
 | ||
|     return Container(
 | ||
|       decoration: BoxDecoration(
 | ||
|         color: Color(color),
 | ||
|         borderRadius: BorderRadius.circular(8),
 | ||
|       ),
 | ||
|       child: Padding(
 | ||
|         padding: EdgeInsets.symmetric(horizontal: horPadding, vertical: 10),
 | ||
|         child: Row(
 | ||
|           children: [
 | ||
|             Container(
 | ||
|               width: 7,
 | ||
|               height: 7,
 | ||
|               decoration: BoxDecoration(
 | ||
|                 color: Colors.blue,
 | ||
|                 borderRadius: BorderRadius.circular(180),
 | ||
|               ),
 | ||
|             ),
 | ||
|             const SizedBox(width: 8),
 | ||
|             Expanded(
 | ||
|               // 添加 Expanded
 | ||
|               child: Text(
 | ||
|                 title,
 | ||
|                 style: const TextStyle(
 | ||
|                   fontSize: 16,
 | ||
|                   fontWeight: FontWeight.bold,
 | ||
|                 ),
 | ||
|               ),
 | ||
|             ),
 | ||
|           ],
 | ||
|         ),
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   /// 单纯标题
 | ||
|   static Widget headerTitle(String title, {bool isRequired = false}) {
 | ||
|     return Container(
 | ||
|       decoration: BoxDecoration(
 | ||
|         color: Colors.white,
 | ||
|         borderRadius: BorderRadius.circular(8),
 | ||
|       ),
 | ||
|       child: Row(
 | ||
|         children: [
 | ||
|           if (isRequired) Text('* ', style: TextStyle(color: Colors.red)),
 | ||
|           Text(
 | ||
|             title,
 | ||
|             style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
 | ||
|           ),
 | ||
|         ],
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   /// 扩展项(根据需求自定义)
 | ||
|   static Widget createCustomItem({
 | ||
|     required Widget child,
 | ||
|     double verticalPadding = 15,
 | ||
|     double horizontalPadding = 0,
 | ||
|   }) {
 | ||
|     return Padding(
 | ||
|       padding: EdgeInsets.symmetric(
 | ||
|         vertical: verticalPadding,
 | ||
|         horizontal: horizontalPadding,
 | ||
|       ),
 | ||
|       child: child,
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   /// 标题加输入框上下排列
 | ||
|   static Widget createBuildMultilineInput(
 | ||
|     String label,
 | ||
|     String hint,
 | ||
|     TextEditingController controller, {
 | ||
|     bool isRequired = false,
 | ||
|   }) {
 | ||
|     return Container(
 | ||
|       height: 130,
 | ||
|       padding: const EdgeInsets.only(top: 8),
 | ||
|       child: Column(
 | ||
|         crossAxisAlignment: CrossAxisAlignment.start,
 | ||
|         children: [
 | ||
|           Row(
 | ||
|             children: [
 | ||
|               if (isRequired) Text('* ', style: TextStyle(color: Colors.red)),
 | ||
|               // 标题
 | ||
|               Expanded(
 | ||
|               child:HhTextStyleUtils.mainTitle(label, fontSize: 15),
 | ||
|               ),
 | ||
|             ],
 | ||
|           ),
 | ||
| 
 | ||
|           const SizedBox(height: 8),
 | ||
|           // 文本输入框,清除默认内边距以与标题左对齐
 | ||
|           Expanded(
 | ||
|             child: TextField(
 | ||
|               autofocus: false,
 | ||
|               controller: controller,
 | ||
|               keyboardType: TextInputType.multiline,
 | ||
|               maxLines: null,
 | ||
|               expands: true,
 | ||
|               style: const TextStyle(fontSize: 15),
 | ||
|               decoration: InputDecoration(
 | ||
|                 hintText: hint,
 | ||
|                 border: InputBorder.none,
 | ||
|                 contentPadding:
 | ||
|                     isRequired
 | ||
|                         ? EdgeInsets.symmetric(horizontal: 10)
 | ||
|                         : EdgeInsets.zero, // 去除默认内边距
 | ||
|               ),
 | ||
|             ),
 | ||
|           ),
 | ||
|         ],
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| 
 | ||
|   ///  分类头部
 | ||
|   static Widget createYesNoSectionTwo({
 | ||
|     required String title,
 | ||
|     required String yesLabel,
 | ||
|     required String noLabel,
 | ||
|     required bool groupValue,
 | ||
|     required bool canClick,
 | ||
|     required BuildContext context,
 | ||
|     required ValueChanged<bool> onChanged,
 | ||
|     double verticalPadding = 15,
 | ||
|     double horizontalPadding = 10,
 | ||
|   }) {
 | ||
|     return Padding(
 | ||
|       padding: EdgeInsets.only(
 | ||
|         top: 0,
 | ||
|         right: horizontalPadding,
 | ||
|         left: horizontalPadding,
 | ||
|         bottom: verticalPadding,
 | ||
|       ),
 | ||
|       child: Container(
 | ||
|         padding: EdgeInsets.symmetric(horizontal: 10),
 | ||
|         decoration: BoxDecoration(
 | ||
|           color: Colors.white,
 | ||
|           borderRadius: BorderRadius.circular(5),
 | ||
|         ),
 | ||
|         child: Row(
 | ||
|           children: [
 | ||
|             Expanded(
 | ||
|               child: Text(
 | ||
|                 title,
 | ||
|                 style: TextStyle(
 | ||
|                   fontSize: 15,
 | ||
|                   fontWeight: FontWeight.bold,
 | ||
|                   color: Colors.black,
 | ||
|                 ),
 | ||
|               ),
 | ||
|             ),
 | ||
|             Row(
 | ||
|               children: [
 | ||
|                 Row(
 | ||
|                   children: [
 | ||
|                     Radio<bool>(
 | ||
|                       activeColor: Colors.blue,
 | ||
|                       value: true,
 | ||
|                       groupValue: groupValue,
 | ||
|                       onChanged: (val) {
 | ||
|                         if (canClick) {
 | ||
|                           onChanged(val!);
 | ||
|                         } else {
 | ||
|                           ToastUtil.showNormal(context, "重大隐患不允许选此项");
 | ||
|                         }
 | ||
|                       },
 | ||
|                       // (val) => onChanged(val!),
 | ||
|                     ),
 | ||
|                     Text(yesLabel),
 | ||
|                   ],
 | ||
|                 ),
 | ||
|                 const SizedBox(width: 16),
 | ||
|                 Row(
 | ||
|                   children: [
 | ||
|                     Radio<bool>(
 | ||
|                       activeColor: Colors.blue,
 | ||
|                       value: false,
 | ||
|                       groupValue: groupValue,
 | ||
|                       onChanged: (val) => onChanged(val!),
 | ||
|                     ),
 | ||
|                     Text(noLabel),
 | ||
|                   ],
 | ||
|                 ),
 | ||
|               ],
 | ||
|             ),
 | ||
|           ],
 | ||
|         ),
 | ||
|       ),
 | ||
|     );
 | ||
|   }
 | ||
| }
 |