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

836 lines
27 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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,
);
}
}