qhd-prevention-flutter/lib/pages/home/tap/item_list_widget.dart

836 lines
27 KiB
Dart
Raw Permalink Normal View History

2025-08-20 09:56:31 +08:00
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<String>? onChanged,
ValueChanged<String>? 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<String>? 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<dynamic>? 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,
);
}
}