316 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			316 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Dart
		
	
	
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),
 | 
						||
                ),
 | 
						||
              ),
 | 
						||
            ),
 | 
						||
        ],
 | 
						||
      ),
 | 
						||
    );
 | 
						||
  }
 | 
						||
}
 |