news_repost_text_view.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import 'package:flutter/material.dart';
  2. import 'package:location/utils/common_expand.dart';
  3. class NewsRepostTextView extends StatefulWidget {
  4. final String? initialValue; // 初始值
  5. final String hintText; // 提示文本
  6. final TextEditingController controller;
  7. final ValueChanged<String>? onChanged; // 文本变化回调
  8. final VoidCallback? onSubmitted; // 提交回调
  9. final int minLength; // 最小字数
  10. final int maxLength; // 最大字数
  11. const NewsRepostTextView({
  12. Key? key,
  13. this.initialValue,
  14. required this.controller,
  15. this.hintText = '请输入用户所违规的场景中,让你感到不适的地方(3~500字以内)',
  16. this.onChanged,
  17. this.onSubmitted,
  18. this.minLength = 3,
  19. this.maxLength = 500,
  20. }) : super(key: key);
  21. @override
  22. _NewsRepostTextViewState createState() => _NewsRepostTextViewState();
  23. }
  24. class _NewsRepostTextViewState extends State<NewsRepostTextView> {
  25. late TextEditingController _controller;
  26. late FocusNode _focusNode;
  27. bool _isValid = true; // 当前输入是否有效
  28. int _currentLength = 0; // 当前字数
  29. @override
  30. void initState() {
  31. super.initState();
  32. _controller = widget.controller;//TextEditingController(text: widget.initialValue);
  33. _focusNode = FocusNode();
  34. _currentLength = widget.initialValue?.length ?? 0;
  35. _validateText();
  36. // 监听文本变化
  37. _controller.addListener(() {
  38. setState(() {
  39. _currentLength = _controller.text.length;
  40. _validateText();
  41. widget.onChanged?.call(_controller.text);
  42. });
  43. });
  44. }
  45. @override
  46. void dispose() {
  47. _controller.dispose();
  48. _focusNode.dispose();
  49. super.dispose();
  50. }
  51. // 验证文本长度
  52. void _validateText() {
  53. final text = _controller.text;
  54. _isValid = text.length >= widget.minLength && text.length <= widget.maxLength;
  55. }
  56. // 获取当前文本状态的提示信息
  57. String get _lengthStatusText {
  58. if (_currentLength < widget.minLength) {
  59. return '至少输入${widget.minLength}个字';
  60. } else if (_currentLength > widget.maxLength) {
  61. return '已超出最大字数限制';
  62. } else {
  63. return '${_currentLength}/${widget.maxLength}';
  64. }
  65. }
  66. // 获取当前文本状态的颜色
  67. Color get _lengthStatusColor {
  68. if (_currentLength < widget.minLength || _currentLength > widget.maxLength) {
  69. return Colors.red;
  70. } else {
  71. return Colors.grey;
  72. }
  73. }
  74. @override
  75. Widget build(BuildContext context) {
  76. return GestureDetector(
  77. onTap: () {
  78. // 点击时收起键盘
  79. FocusScopeNode currentFocus = FocusScope.of(context);
  80. if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
  81. FocusManager.instance.primaryFocus?.unfocus();
  82. }
  83. },
  84. child: Column(
  85. crossAxisAlignment: CrossAxisAlignment.start,
  86. children: [
  87. TextField(
  88. controller: _controller,
  89. focusNode: _focusNode,
  90. maxLines: null, // 允许多行输入
  91. minLines: 4, // 最小显示行数
  92. maxLength: widget.maxLength, // 最大字数限制(UI层限制)
  93. keyboardType: TextInputType.multiline,
  94. decoration: InputDecoration(
  95. hintText: widget.hintText,
  96. border: OutlineInputBorder(
  97. borderSide: BorderSide(
  98. color: "#12C2C2C2".color,
  99. width: 1.0,
  100. ),
  101. ),
  102. enabledBorder: OutlineInputBorder(
  103. borderSide: BorderSide(
  104. color: "#12C2C2C2".color,
  105. width: 1.0,
  106. ),
  107. ),
  108. focusedBorder: OutlineInputBorder(
  109. borderSide: BorderSide(
  110. color: _isValid ? "#12C2C2C2".color : Colors.red,
  111. width: 2.0,
  112. ),
  113. borderRadius: BorderRadius.all(Radius.circular(10))
  114. ),
  115. counterText: '', // 隐藏默认计数器
  116. ),
  117. onSubmitted: (_) => widget.onSubmitted?.call(),
  118. ),
  119. SizedBox(height: 8.0),
  120. Row(
  121. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  122. children: [
  123. Text(
  124. _lengthStatusText,
  125. style: TextStyle(color: _lengthStatusColor),
  126. ),
  127. if (!_isValid)
  128. Text(
  129. '字数不符合要求',
  130. style: TextStyle(color: Colors.red),
  131. ),
  132. ],
  133. ),
  134. ],
  135. ),
  136. );
  137. }
  138. }