import 'dart:io'; import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/pages/http/ApiService.dart'; import 'package:qhd_prevention/tools/tools.dart'; class ItemListWidget { static const Color detailtextColor = Colors.black54; /// 单行水平排列: /// - 可编辑时:标题 + TextField /// - 不可编辑时:标题 + 带省略号的文本 static Widget singleLineTitleText({ required String label, // 标题文本 required bool isEditable, // 是否可编辑 TextEditingController? controller, // 编辑时使用的控制器 String? text, // 不可编辑时显示的文本 String hintText = '请输入', double fontSize = 15, // 字体大小 bool isRequired = true, bool strongRequired = false, ValueChanged? onChanged, ValueChanged? onFieldSubmitted, /// 强制必选 不受是否可以编译和是否必选影响 TextInputType keyboardType = TextInputType.text, }) { return Container( padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), child: Row( mainAxisAlignment: isEditable ? MainAxisAlignment.start : MainAxisAlignment.spaceBetween, children: [ Row( children: [ if ((isRequired && isEditable) || strongRequired) Text('* ', style: TextStyle(color: Colors.red)), Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), ], ), // 显示标题 const SizedBox(width: 8), isEditable ? Expanded( child: TextField( autofocus: false, controller: controller, onChanged: onChanged, // <--- 直接回传实时值 keyboardType: keyboardType, style: TextStyle(fontSize: fontSize), maxLines: 1, decoration: InputDecoration( isDense: true, hintText: hintText, contentPadding: EdgeInsets.symmetric(vertical: 8), ), ), ) : Expanded(child: Text( text ?? '', maxLines: 5, style: TextStyle(fontSize: fontSize, color: detailtextColor), textAlign: TextAlign.right, overflow: TextOverflow.ellipsis, // 超出省略 )), ], ), ); } /// 多行垂直排列: /// - 可编辑时:标题 + 可扩展的多行 TextField /// - 不可编辑时:标题 + 带滚动的多行文本 static Widget multiLineTitleTextField({ required String label, // 标题文本 required bool isEditable, // 是否可编辑 TextEditingController? controller, // 编辑时使用的控制器 String? text, // 不可编辑时显示的文本 double fontSize = 15, // 字体大小 double height = 110, // 整体高度 bool isRequired = true, String hintText = '请输入', ValueChanged? onChanged, }) { return Container( // 统一左右 padding,保证标题和内容在同一左侧基线 padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), height: height, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ if (isRequired && isEditable) Text('* ', style: TextStyle(color: Colors.red)), Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 8), Expanded( child: isEditable ? TextField( autofocus: false, controller: controller, keyboardType: TextInputType.multiline, maxLines: null, expands: true, onChanged: onChanged, // 垂直顶部对齐 textAlignVertical: TextAlignVertical.top, style: TextStyle(fontSize: fontSize), decoration: InputDecoration( hintText: hintText, // 去掉 TextField 默认内边距 //contentPadding: EdgeInsets.zero, border: InputBorder.none, ), ) : SingleChildScrollView( // 去掉多余的 padding padding: EdgeInsets.zero, child: Text( text ?? '', style: TextStyle( fontSize: fontSize, color: detailtextColor, ), ), ), ), ], ), ); } /// 单行可点击选择: /// - 可编辑时:标题 + “请选择”提示 + 右箭头 /// - 不可编辑时:标题 + 文本内容 static Widget selectableLineTitleTextRightButton({ required String label, // 标题文本 required bool isEditable, // 是否可点击 required String text, // 显示内容或提示 VoidCallback? onTap, // 点击回调 double fontSize = 15, // 字体大小 bool isClean = false, VoidCallback? onTapClean, // 清除回调 bool isRequired = true, String cleanText = '清除', double horizontalnum=12, }) { return InkWell( onTap: isEditable ? onTap : null, child: Container( padding: EdgeInsets.symmetric(vertical: 5, horizontal: horizontalnum), child: Row( children: [ // 1. 标题 Row( children: [ if (isRequired && isEditable) Text('* ', style: TextStyle(color: Colors.red)), Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), ], ), if (isClean) Column( children: [ CustomButton( text: cleanText, height: 20, padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0), textStyle: TextStyle(fontSize: 11, color: Colors.white), borderRadius: 10, backgroundColor: cleanText == '清除' ? Colors.red : Colors.green, onPressed: onTapClean, ), SizedBox(height: 20), ], ), const SizedBox(width: 8), Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Flexible( child: Text( text.isNotEmpty ? text : '请选择', maxLines: 5, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: fontSize, color: isEditable ? Colors.black : detailtextColor, ), ), ), if (isEditable) const Padding( padding: EdgeInsets.only(left: 4), child: Icon(Icons.chevron_right, size: 20), ), ], ), ), ], ), ), ); } /// 单行可点击选择: /// - 可编辑时:标题 + “请选择”提示 + 文本框 /// - 不可编辑时:标题 + 文本内容 static Widget selectableLineTitleTextField({ required String label, required bool isEditable, required String text, VoidCallback? onTap, double fontSize = 15, bool isClean = false, VoidCallback? onTapClean, bool isRequired = true, String cleanText = '清除', TextEditingController? controller, }) { return InkWell( onTap: isEditable ? onTap : null, child: Container( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // 标题部分 Row( mainAxisSize: MainAxisSize.min, children: [ if (isRequired && isEditable) const Text('* ', style: TextStyle(color: Colors.red)), Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), ], ), // const SizedBox(width: 5), // 右侧 TextField + 清除按钮 Expanded( child: Row( children: [ // 清除按钮 if (isClean && onTapClean != null) Column( children: [ CustomButton( text: cleanText, height: 20, padding: EdgeInsets.symmetric( horizontal: 10, vertical: 0, ), textStyle: TextStyle( fontSize: 11, color: Colors.white, ), borderRadius: 10, backgroundColor: cleanText == '清除' ? Colors.red : Colors.green, onPressed: onTapClean, ), SizedBox(height: 20), ], ), // 输入框 Expanded( child: TextField( controller: controller, autofocus: false, style: TextStyle(fontSize: fontSize), decoration: InputDecoration( hintText: '请输入', isCollapsed: true, contentPadding: const EdgeInsets.symmetric(vertical: 8), border: InputBorder.none, ), ), ), ], ), ), ], ), ), ); } /// 两行垂直布局: /// 第一行:可点击选择(带箭头)或仅显示标题 /// 第二行:多行输入框或多行文本展示 static Widget twoRowSelectableTitleText({ required String label, // 第一行标题 required bool isEditable, // 是否可编辑 required String text, // 显示内容或提示 TextEditingController? controller, // 第二行编辑控制器 VoidCallback? onTap, // 第一行点击回调 double fontSize = 15, // 字体大小 double row2Height = 80, // 第二行高度 bool isRequired = true, }) { return Container( padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 第一行:可点击区域或纯文本标题 InkWell( onTap: isEditable ? onTap : null, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ if (isRequired && isEditable) Text('* ', style: TextStyle(color: Colors.red)), Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), ], ), Row( children: [ Text( isEditable ? '请选择' : '', style: TextStyle( fontSize: fontSize, color: isEditable ? Colors.black : detailtextColor, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (isEditable) const Icon(Icons.chevron_right), ], ), ], ), ), const SizedBox(height: 8), Container( height: row2Height, padding: const EdgeInsets.symmetric(vertical: 8), child: isEditable ? TextField( autofocus: false, controller: controller, keyboardType: TextInputType.multiline, maxLines: null, expands: true, style: TextStyle(fontSize: fontSize), decoration: InputDecoration( hintText: '请输入', //contentPadding: EdgeInsets.zero, border: InputBorder.none, ), ) : SingleChildScrollView( padding: EdgeInsets.zero, child: Text( text, style: TextStyle( fontSize: fontSize, color: detailtextColor, ), ), ), ), ], ), ); } /// 两行垂直布局: /// 标题 + 按钮 /// 第二行:多行输入框或多行文本展示 static Widget twoRowButtonTitleText({ required String label, // 第一行标题 required bool isEditable, // 是否可编辑 required String text, // 显示内容或提示 TextEditingController? controller, // 第二行编辑控制器 required VoidCallback? onTap, // 第一行点击回调 required String hintText, double fontSize = 15, // 字体大小 double row2Height = 80, // 第二行高度 bool isRequired = true, }) { return Container( padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 第一行:标题 + 按钮 InkWell( child: Row( children: [ Flexible( fit: FlexFit.loose, // loose 模式下它可以比最大宽度更小 child: Row( children: [ if (isRequired && isEditable) Text('* ', style: TextStyle(color: Colors.red)), Flexible( child: Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ), const SizedBox(width: 8), if (isEditable) CustomButton( text: "选择其他", height: 30, padding: const EdgeInsets.symmetric( vertical: 2, horizontal: 5, ), backgroundColor: Colors.green, onPressed: onTap, ), ], ), ), const SizedBox(height: 8), Container( height: row2Height, padding: const EdgeInsets.symmetric(vertical: 8), child: isEditable ? TextField( autofocus: false, controller: controller, keyboardType: TextInputType.multiline, maxLines: null, expands: true, style: TextStyle(fontSize: fontSize), decoration: InputDecoration( hintText: hintText, //contentPadding: EdgeInsets.zero, border: InputBorder.none, ), ) : SingleChildScrollView( padding: EdgeInsets.zero, child: Text( text, style: TextStyle( fontSize: fontSize, color: detailtextColor, ), ), ), ), ], ), ); } /// 单行布局: /// 标题 + 文字 + 按钮 static Widget OneRowButtonTitleText({ required String label, // 标题 required String text, // 显示内容或提示 required VoidCallback? onTap, // 第一行点击回调 double fontSize = 15, // 字体大小 }) { return Container( padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Row( children: [ Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), SizedBox(width: 15), Expanded( child: Text( text, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: fontSize, color: detailtextColor, ), ), ), ], ), ), CustomButton( text: "分析详情", height: 30, padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5), backgroundColor: Colors.green, onPressed: onTap, ), ], ), ); } /// 单行布局: /// 标题 + 按钮 static Widget OneRowButtonTitle({ required String label, // 标题 required String buttonText, // 按钮文字 required VoidCallback onTap, // 第一行点击回调 double fontSize = 15, // 字体大小 Color btnColor = Colors.blue, bool isRequired = false, }) { return Container( padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Text("* ", style: TextStyle(color: Colors.red)), Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), ], ), CustomButton( text: buttonText, height: 30, padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5), backgroundColor: btnColor, onPressed: onTap, ), ], ), ); } /// 单行布局: /// 标题 + 网络图片 static Widget OneRowImageTitle({ required String label, // 标题 required String imgPath, // 图片路径 double fontSize = 15, // 字体大小 Color btnColor = Colors.blue, bool isRequired = false, String text = '', void Function(String)? onTapCallBack, }) { return Container( padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), ], ), Column( children: [ GestureDetector( onTap: () { if (onTapCallBack != null) onTapCallBack('${ApiService.baseImgPath}$imgPath'); }, child: imgPath.isNotEmpty ? Image.network( '${ApiService.baseImgPath}${imgPath}', width: 50, height: 50, ) : SizedBox(), ), if (text.isNotEmpty) Text(text) ], ) ], ), ); } /// 两行垂直布局: /// 标题 /// 第二行:图片 static Widget twoRowTitleAndImages({ required String title, // 第一行标题 required List? imageUrls, // 传入 pd['CONTENT_IMG_PATH'] double row2Height = 80, // 第二行高度 double fontSize = 15, // 字体大小 void Function(String)? onTapCallBack, bool isRequired = true, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题部分 Padding( padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12), child: Text( title, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), ), ), // 图片横向滚动区域 SizedBox( height: 80, // 图片区域固定高度 child: ListView.builder( padding: EdgeInsets.symmetric(horizontal: 12), scrollDirection: Axis.horizontal, itemCount: imageUrls?.length, itemBuilder: (context, index) { return Container( margin: const EdgeInsets.only(right: 8), // 图片间距 child: ClipRRect( borderRadius: BorderRadius.circular(8), child: GestureDetector( onTap: () { if (onTapCallBack != null) onTapCallBack('${ApiService.baseImgPath}${imageUrls![index] ?? ''}'); }, child: Image.network( '${ApiService.baseImgPath}${imageUrls![index] ?? ''}', width: 80, // 图片宽度 height: 80, // 图片高度 fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return Container( width: 80, height: 80, color: Colors.grey[200], child: const Center(child: CircularProgressIndicator()), ); }, errorBuilder: (context, error, stackTrace) { return Container( width: 80, height: 80, color: Colors.transparent, child: SizedBox(), ); }, ), ) ), ); }, ), ), ], ); } /// 多行垂直布局: /// 标题+按钮 /// 编辑框列表,多个编辑框可删除 static Widget mulRowTitleAndTextField({ required String label, // 第一行标题 required bool isEditable, // 是否可编辑 required String text, // 显示内容或提示 TextEditingController? controller, // 第二行编辑控制器 required VoidCallback? onTap, // 第一行点击回调 required String hintText, double fontSize = 15, // 字体大小 double row2Height = 80, // 第二行高度 bool isRequired = true, }) { return Container( padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 第一行:标题 + 按钮 InkWell( child: Row( children: [ Flexible( fit: FlexFit.loose, // loose 模式下它可以比最大宽度更小 child: Row( children: [ if (isRequired && isEditable) Text('* ', style: TextStyle(color: Colors.red)), Flexible( child: Text( label, style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ), const SizedBox(width: 8), if (isEditable) CustomButton( text: "选择其他", height: 30, padding: const EdgeInsets.symmetric( vertical: 2, horizontal: 5, ), backgroundColor: Colors.green, onPressed: onTap, ), ], ), ), const SizedBox(height: 8), Container( height: row2Height, padding: const EdgeInsets.symmetric(vertical: 8), child: isEditable ? TextField( autofocus: false, controller: controller, keyboardType: TextInputType.multiline, maxLines: null, expands: true, style: TextStyle(fontSize: fontSize), decoration: InputDecoration( hintText: hintText, //contentPadding: EdgeInsets.zero, border: InputBorder.none, ), ) : SingleChildScrollView( padding: EdgeInsets.zero, child: Text( text, style: TextStyle( fontSize: fontSize, color: detailtextColor, ), ), ), ), ], ), ); } static Widget itemContainer(Widget child, {double horizontal = 12}) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: 8), child: child, ); } }