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

316 lines
9.5 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 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/dotted_border_box.dart';
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 [],
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);
}
} 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());
});
}
@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());
});
}
}
@override
void dispose() {
for (var controller in _controllers) {
controller.dispose();
}
for (var node in _focusNodes) {
node.dispose();
}
super.dispose();
}
// _addTextField 现在支持传入初始文本
void _addTextField([String initialText = '']) {
setState(() {
final newController = TextEditingController(text: initialText);
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();
}
});
});
}
void _removeTextField(int index) async {
if (_controllers.length <= 1) return;
final confirmed = await CustomAlertDialog.showConfirm(
context,
title: '提示',
content: '确定删除检查情况吗?',
cancelText: '取消',
confirmText: '确定',
);
if (!confirmed) return;
setState(() {
_controllers[index].dispose();
_focusNodes[index].dispose();
_controllers.removeAt(index);
_focusNodes.removeAt(index);
widget.onTextsChanged(_getAllTexts());
});
}
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(),
),
],
),
),
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),
child: SizedBox(
width: double.maxFinite,
child: DottedBorderBox(
child: Text(
c,
style: TextStyle(
fontSize: widget.fontSize,
color: Colors.grey[600],
),
),
)),
);
}).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),
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),
),
)),
),
// 删除按钮(叠加在左上角)
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),
),
),
),
],
),
);
}
}