dialog_number.dart 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart';
  3. import 'package:get/get.dart';
  4. import 'alert_dialog.dart';
  5. /// 小弹窗输入
  6. class VDialogNumber extends StatefulWidget {
  7. /// 标题
  8. final String? title;
  9. /// 描述
  10. final String? description;
  11. /// 输入占位符
  12. final String? placeholder;
  13. /// 初始值
  14. final String? initialValue;
  15. const VDialogNumber({
  16. Key? key,
  17. this.title,
  18. this.description,
  19. this.placeholder,
  20. this.initialValue,
  21. }) : super(key: key);
  22. @override
  23. State<VDialogNumber> createState() => _VDialogNumberState();
  24. Future<String?> show<String>() => VAlertDialog.showDialog<String>(this);
  25. }
  26. class _VDialogNumberState extends State<VDialogNumber> {
  27. late TextEditingController _controller;
  28. double _value = 0;
  29. @override
  30. void initState() {
  31. super.initState();
  32. _controller = TextEditingController(text: widget.initialValue);
  33. _value = double.tryParse(_controller.text) ?? 0;
  34. }
  35. @override
  36. Widget build(BuildContext context) {
  37. return VAlertDialog(
  38. title: widget.title,
  39. width: 440,
  40. contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
  41. content: _buildContent(context),
  42. showCancel: true,
  43. onConfirm: () {
  44. Get.back(result: _controller.text);
  45. },
  46. );
  47. }
  48. Widget _buildContent(BuildContext context) {
  49. final children = <Widget>[];
  50. if (widget.description != null) {
  51. children.add(
  52. Padding(
  53. padding: const EdgeInsets.symmetric(horizontal: 4),
  54. child: Text(
  55. widget.description!,
  56. style: const TextStyle(color: Colors.black87, fontSize: 18),
  57. ),
  58. ),
  59. );
  60. children.add(const SizedBox(height: 8));
  61. } else {
  62. children.add(const SizedBox(height: 12));
  63. }
  64. children.add(_buildInputWidget(context));
  65. return SingleChildScrollView(
  66. child: Column(
  67. mainAxisSize: MainAxisSize.min,
  68. crossAxisAlignment: CrossAxisAlignment.start,
  69. children: children,
  70. ),
  71. );
  72. }
  73. Widget _buildInputWidget(BuildContext context) {
  74. const fontSize = 20.0;
  75. const height = 56.0;
  76. return SizedBox(
  77. height: height,
  78. child: Row(
  79. children: [
  80. IconButton(
  81. icon: Icon(
  82. Icons.remove,
  83. color: _value == 0 ? Colors.grey : Theme.of(context).primaryColor,
  84. ),
  85. onPressed: _value == 0
  86. ? null
  87. : () {
  88. setState(() {
  89. _value = (_value - 1).clamp(0, 999); // 减1,并限制在0到999之间
  90. _controller.text = _value.toString();
  91. _moveCursorToEnd();
  92. });
  93. },
  94. ),
  95. Expanded(
  96. child: TextField(
  97. controller: _controller,
  98. readOnly: false,
  99. textAlign: TextAlign.center,
  100. autofocus: true,
  101. keyboardType: const TextInputType.numberWithOptions(
  102. decimal: true), // 允许输入数字和小数点
  103. inputFormatters: [
  104. FilteringTextInputFormatter.allow(
  105. RegExp(r'^\d+\.?\d{0,2}'),
  106. ), // 只允许输入数字和小数点,俩位小数
  107. ],
  108. style: const TextStyle(fontSize: fontSize),
  109. decoration: InputDecoration(
  110. border: const UnderlineInputBorder(
  111. borderRadius: BorderRadius.zero,
  112. borderSide: BorderSide(),
  113. ),
  114. enabledBorder: const UnderlineInputBorder(
  115. borderRadius: BorderRadius.zero,
  116. borderSide: BorderSide(
  117. color: Colors.black54,
  118. ),
  119. ),
  120. focusedBorder: UnderlineInputBorder(
  121. borderRadius: BorderRadius.zero,
  122. borderSide: BorderSide(
  123. color: Theme.of(context).primaryColor.withOpacity(.4),
  124. ),
  125. ),
  126. filled: true,
  127. fillColor: Colors.white,
  128. contentPadding: const EdgeInsets.symmetric(
  129. vertical: (height - fontSize * 1.2) / 2,
  130. horizontal: 8,
  131. ),
  132. hintStyle: const TextStyle(fontSize: fontSize),
  133. labelStyle: const TextStyle(fontSize: fontSize),
  134. hintText: widget.placeholder,
  135. isCollapsed: true,
  136. ),
  137. onChanged: (value) {
  138. setState(() {
  139. _value = double.tryParse(value) ?? 0; // 将_value的类型改为double
  140. });
  141. },
  142. onSubmitted: (value) {
  143. Get.back(result: value);
  144. },
  145. ),
  146. ),
  147. IconButton(
  148. icon: Icon(
  149. Icons.add,
  150. color:
  151. _value == 999 ? Colors.grey : Theme.of(context).primaryColor,
  152. ),
  153. onPressed: _value == 999
  154. ? null
  155. : () {
  156. setState(() {
  157. _value = (_value + 1).clamp(0, 999); // 加1,并限制在0到999之间
  158. _controller.text = _value.toString();
  159. _moveCursorToEnd();
  160. });
  161. },
  162. ),
  163. ],
  164. ),
  165. );
  166. }
  167. void _moveCursorToEnd() {
  168. final text = _controller.text;
  169. _controller.value = _controller.value.copyWith(
  170. selection: TextSelection.collapsed(offset: text.length),
  171. );
  172. }
  173. }