fullview_input.dart 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import 'package:flutter/material.dart';
  2. /// 全屏输入框
  3. class VFullViewInput extends StatefulWidget {
  4. /// 初始值
  5. final String? value;
  6. /// 占位文本
  7. final String? placeholder;
  8. /// 值变更事件
  9. final ValueChanged<String>? onChanged;
  10. /// 是否可清除(一键)
  11. final bool? clearable;
  12. /// 前缀
  13. final Widget? prefix;
  14. /// 后缀
  15. final Widget? suffix;
  16. /// 字号
  17. final double? fontSize;
  18. const VFullViewInput({
  19. super.key,
  20. this.value,
  21. this.placeholder,
  22. this.onChanged,
  23. this.clearable,
  24. this.prefix,
  25. this.suffix,
  26. this.fontSize,
  27. });
  28. @override
  29. State<StatefulWidget> createState() => _InputState();
  30. }
  31. class _InputState extends State<VFullViewInput> {
  32. late String value;
  33. late final double fontSize;
  34. @override
  35. void initState() {
  36. fontSize = widget.fontSize ?? 20;
  37. value = widget.value ?? "";
  38. super.initState();
  39. }
  40. @override
  41. Widget build(BuildContext context) {
  42. return LayoutBuilder(
  43. builder: (_, c) {
  44. final height = c.maxHeight;
  45. final borderRadius = height / 2;
  46. final border = OutlineInputBorder(
  47. borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
  48. borderSide: BorderSide.none,
  49. );
  50. final suffix = _buildSuffix();
  51. return SizedBox(
  52. height: height,
  53. child: TextField(
  54. controller: TextEditingController(text: value),
  55. readOnly: true,
  56. style: TextStyle(fontSize: fontSize),
  57. decoration: InputDecoration(
  58. border: border,
  59. enabledBorder: border,
  60. focusedBorder: border,
  61. filled: true,
  62. fillColor: Colors.grey.shade300,
  63. contentPadding: EdgeInsets.symmetric(
  64. vertical: (height - fontSize * 1.2) / 2,
  65. horizontal: borderRadius,
  66. ),
  67. hintStyle: TextStyle(fontSize: fontSize),
  68. labelStyle: TextStyle(fontSize: fontSize),
  69. hintText: widget.placeholder,
  70. prefixIcon: widget.prefix,
  71. suffixIcon: suffix != null
  72. ? Row(
  73. mainAxisSize: MainAxisSize.min,
  74. children: [
  75. suffix,
  76. SizedBox(width: borderRadius / 4),
  77. ],
  78. )
  79. : null,
  80. isCollapsed: true,
  81. ),
  82. onTap: () {
  83. _onTap(height);
  84. },
  85. ),
  86. );
  87. },
  88. );
  89. }
  90. void _onTap(double height) async {
  91. final result = await Navigator.of(context).push<String>(
  92. MaterialPageRoute(
  93. fullscreenDialog: true,
  94. builder: (context) {
  95. return _InputDialog(
  96. height: height,
  97. fontSize: fontSize,
  98. title: widget.placeholder ?? "请输入",
  99. value: value,
  100. );
  101. },
  102. ),
  103. );
  104. if (result != null) {
  105. setState(() {
  106. value = result;
  107. widget.onChanged?.call(value);
  108. });
  109. }
  110. }
  111. Widget? _buildSuffix() {
  112. if (widget.suffix != null) {
  113. return widget.suffix;
  114. }
  115. if (widget.clearable == true && value.isNotEmpty) {
  116. return IconButton(
  117. icon: Icon(
  118. Icons.close,
  119. size: fontSize * 1.2,
  120. color: Colors.grey,
  121. ),
  122. onPressed: () {
  123. setState(() {
  124. value = "";
  125. widget.onChanged?.call(value);
  126. });
  127. },
  128. );
  129. }
  130. return null;
  131. }
  132. }
  133. class _InputDialog extends StatelessWidget {
  134. late final _controller = TextEditingController(text: value);
  135. final String value;
  136. final String title;
  137. final double fontSize;
  138. final double height;
  139. _InputDialog({
  140. super.key,
  141. required this.value,
  142. required this.title,
  143. required this.fontSize,
  144. required this.height,
  145. });
  146. @override
  147. Widget build(BuildContext context) {
  148. return Scaffold(
  149. appBar: AppBar(
  150. title: Text(title),
  151. titleTextStyle: TextStyle(color: Colors.grey.shade700, fontSize: 20),
  152. centerTitle: true,
  153. leading: IconButton(
  154. icon: const Icon(Icons.arrow_back, size: 28),
  155. onPressed: () {
  156. Navigator.of(context).pop();
  157. },
  158. ),
  159. actions: [
  160. IconButton(
  161. icon: const Icon(Icons.check, size: 28),
  162. onPressed: () {
  163. _onCompleted(context);
  164. },
  165. )
  166. ],
  167. ),
  168. body: Container(
  169. alignment: Alignment.topCenter,
  170. padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
  171. child: SizedBox(
  172. height: height,
  173. child: _buildInput(context),
  174. ),
  175. ),
  176. );
  177. }
  178. Widget _buildInput(BuildContext context) {
  179. final borderRadius = height / 2;
  180. return TextField(
  181. autofocus: true,
  182. controller: _controller,
  183. style: TextStyle(fontSize: fontSize),
  184. decoration: InputDecoration(
  185. filled: true,
  186. fillColor: Colors.grey.shade300,
  187. focusedBorder: OutlineInputBorder(
  188. borderRadius: BorderRadius.circular(borderRadius),
  189. // borderSide: BorderSide(color: Theme.of(context).primaryColor),
  190. borderSide: BorderSide.none,
  191. ),
  192. contentPadding: EdgeInsets.symmetric(
  193. vertical: (height - fontSize * 1.2) / 2,
  194. horizontal: borderRadius,
  195. ),
  196. hintStyle: TextStyle(fontSize: fontSize),
  197. labelStyle: TextStyle(fontSize: fontSize),
  198. ),
  199. onEditingComplete: () {
  200. _onCompleted(context);
  201. },
  202. onChanged: (value) {
  203. _controller.text = value;
  204. },
  205. );
  206. }
  207. void _onCompleted(BuildContext context) {
  208. Navigator.of(context).pop(_controller.text);
  209. }
  210. }