flutter_integrated_whb/lib/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart

316 lines
9.5 KiB
Dart
Raw Normal View History

2025-08-14 15:05:48 +08:00
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
2025-08-14 18:14:15 +08:00
import 'package:qhd_prevention/customWidget/dotted_border_box.dart';
2025-08-14 15:05:48 +08:00
class MultiTextFieldWithTitle extends StatefulWidget {
final String label;
final List<String> texts;
final bool isEditable;
final String hintText;
final double fontSize;
final bool isRequired;
final ValueChanged<List<String>> onTextsChanged;
const MultiTextFieldWithTitle({
super.key,
required this.label,
required this.isEditable,
required this.hintText,
required this.onTextsChanged,
this.fontSize = 15,
this.texts = const [],
2025-08-14 15:05:48 +08:00
this.isRequired = true,
});
@override
State<MultiTextFieldWithTitle> createState() =>
_MultiTextFieldWithTitleState();
}
class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
final List<TextEditingController> _controllers = [];
final List<FocusNode> _focusNodes = [];
@override
void initState() {
super.initState();
// 根据传入的初始 texts 初始化 controllers可编辑时也赋值
if (widget.texts.isNotEmpty) {
for (final t in widget.texts) {
final controller = TextEditingController(text: t);
final node = FocusNode();
controller.addListener(() {
widget.onTextsChanged(_getAllTexts());
});
_controllers.add(controller);
_focusNodes.add(node);
2025-08-16 14:03:56 +08:00
}
} else {
// 如果没有初始值,至少创建一个空的控制器(可编辑场景需要输入框)
final controller = TextEditingController();
final node = FocusNode();
controller.addListener(() {
widget.onTextsChanged(_getAllTexts());
});
_controllers.add(controller);
_focusNodes.add(node);
}
// 触发一次回调,确保父组件拿到初始值
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) widget.onTextsChanged(_getAllTexts());
2025-08-16 14:10:57 +08:00
});
2025-08-16 14:03:56 +08:00
}
@override
void didUpdateWidget(covariant MultiTextFieldWithTitle oldWidget) {
super.didUpdateWidget(oldWidget);
// 当外部传入的 texts 发生变化(例如父组件替换了初始列表),
// 且当前 controllers 与之不一致时,进行同步更新。
// 为避免覆盖用户正在编辑的数据,这里只在长度或内容明显不同时才同步。
final newTexts = widget.texts;
bool needSync = false;
if (newTexts.length != _controllers.length) {
needSync = true;
} else {
// 长度相等时比较内容
for (var i = 0; i < newTexts.length; i++) {
final controllerText = _controllers[i].text;
if (controllerText != newTexts[i]) {
needSync = true;
break;
}
}
}
if (needSync) {
// 清理旧的 controllers / nodes
for (var c in _controllers) {
c.dispose();
}
for (var n in _focusNodes) {
n.dispose();
}
_controllers.clear();
_focusNodes.clear();
// 重新创建
if (newTexts.isNotEmpty) {
for (final t in newTexts) {
final controller = TextEditingController(text: t);
final node = FocusNode();
controller.addListener(() {
widget.onTextsChanged(_getAllTexts());
});
_controllers.add(controller);
_focusNodes.add(node);
}
} else {
final controller = TextEditingController();
final node = FocusNode();
controller.addListener(() {
widget.onTextsChanged(_getAllTexts());
});
_controllers.add(controller);
_focusNodes.add(node);
}
// 通知父组件
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) widget.onTextsChanged(_getAllTexts());
});
}
}
2025-08-14 15:05:48 +08:00
@override
void dispose() {
for (var controller in _controllers) {
controller.dispose();
}
for (var node in _focusNodes) {
node.dispose();
}
super.dispose();
}
// _addTextField 现在支持传入初始文本
void _addTextField([String initialText = '']) {
2025-08-14 15:05:48 +08:00
setState(() {
final newController = TextEditingController(text: initialText);
2025-08-14 15:05:48 +08:00
final newFocusNode = FocusNode();
newController.addListener(() {
widget.onTextsChanged(_getAllTexts());
});
_controllers.add(newController);
_focusNodes.add(newFocusNode);
widget.onTextsChanged(_getAllTexts());
// 自动聚焦到新创建的输入框(延迟到下一帧)
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
final idx = _controllers.length - 1;
_focusNodes[idx].requestFocus();
}
});
2025-08-14 15:05:48 +08:00
});
}
void _removeTextField(int index) async {
if (_controllers.length <= 1) return;
final confirmed = await CustomAlertDialog.showConfirm(
context,
title: '提示',
content: '确定删除检查情况吗?',
cancelText: '取消',
confirmText: '确定',
2025-08-14 15:05:48 +08:00
);
if (!confirmed) return;
setState(() {
_controllers[index].dispose();
_focusNodes[index].dispose();
_controllers.removeAt(index);
_focusNodes.removeAt(index);
widget.onTextsChanged(_getAllTexts());
});
2025-08-14 15:05:48 +08:00
}
List<String> _getAllTexts() {
return _controllers.map((c) => c.text).toList();
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行
InkWell(
child: Row(
children: [
Flexible(
fit: FlexFit.loose,
child: Row(
children: [
if (widget.isRequired && widget.isEditable)
Text('* ', style: TextStyle(color: Colors.red)),
Flexible(
child: Text(
widget.label,
style: TextStyle(
fontSize: widget.fontSize,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
const SizedBox(width: 8),
if (widget.isEditable)
CustomButton(
text: " 添加 ",
height: 30,
padding: const EdgeInsets.symmetric(
vertical: 2,
horizontal: 5,
),
backgroundColor: Colors.blue,
onPressed: () => _addTextField(),
2025-08-14 15:05:48 +08:00
),
],
),
),
const SizedBox(height: 8),
// 输入框区域 - 高度自适应
Column(
children: [
// 可编辑状态
if (widget.isEditable)
..._controllers.asMap().entries.map((entry) {
final index = entry.key;
return _buildTextFieldWithDelete(index);
}).toList(),
// 不可编辑状态
if (!widget.isEditable)
...widget.texts.map((c) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
2025-08-14 18:14:15 +08:00
child: SizedBox(
width: double.maxFinite,
child: DottedBorderBox(
child: Text(
c,
style: TextStyle(
fontSize: widget.fontSize,
color: Colors.grey[600],
),
2025-08-14 18:14:15 +08:00
),
)),
2025-08-14 15:05:48 +08:00
);
}).toList(),
],
),
],
),
);
}
Widget _buildTextFieldWithDelete(int index) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Stack(
children: [
// 输入框
Padding(
padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7),
2025-08-14 18:14:15 +08:00
child: SizedBox(
width: double.maxFinite,
child: DottedBorderBox(
child: TextField(
controller: _controllers[index],
decoration: InputDecoration(hintText: widget.hintText),
focusNode: _focusNodes[index],
keyboardType: TextInputType.multiline,
maxLines: 3,
minLines: 3,
style: TextStyle(fontSize: widget.fontSize),
),
)),
2025-08-14 15:05:48 +08:00
),
// 删除按钮(叠加在左上角)
if (index > 0)
Positioned(
top: 0,
left: 0,
child: GestureDetector(
onTap: () => _removeTextField(index),
child: Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.all(4),
child: const Icon(Icons.close, size: 10, color: Colors.white),
),
),
),
],
),
);
}
}