text_field.dart 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import 'dart:async';
  2. import 'package:flutter/foundation.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/services.dart';
  5. import 'package:keyboard_actions/keyboard_actions.dart';
  6. import 'package:liftmanager/res/resources.dart';
  7. import 'package:rxdart/rxdart.dart';
  8. import 'load_image.dart';
  9. /// 登录模块的输入框封装
  10. class MyTextField extends StatefulWidget {
  11. const MyTextField(
  12. {Key key,
  13. @required this.controller,
  14. this.maxLength: 16,
  15. this.autoFocus: false,
  16. this.keyboardType: TextInputType.text,
  17. this.hintText: "",
  18. this.focusNode,
  19. this.isInputPwd: false,
  20. this.getVCode,
  21. this.config,
  22. this.keyName})
  23. : super(key: key);
  24. final TextEditingController controller;
  25. final int maxLength;
  26. final bool autoFocus;
  27. final TextInputType keyboardType;
  28. final String hintText;
  29. final FocusNode focusNode;
  30. final bool isInputPwd;
  31. final Function() getVCode;
  32. final KeyboardActionsConfig config;
  33. /// 用于集成测试寻找widget
  34. final String keyName;
  35. @override
  36. _MyTextFieldState createState() => _MyTextFieldState();
  37. }
  38. class _MyTextFieldState extends State<MyTextField> {
  39. bool _isShowPwd = false;
  40. bool _isShowDelete;
  41. bool _isClick = true;
  42. /// 倒计时秒数
  43. final int second = 30;
  44. /// 当前秒数
  45. int s;
  46. StreamSubscription _subscription;
  47. @override
  48. void initState() {
  49. super.initState();
  50. /// 获取初始化值
  51. _isShowDelete = widget.controller.text.isEmpty;
  52. /// 监听输入改变
  53. widget.controller.addListener(() {
  54. setState(() {
  55. _isShowDelete = widget.controller.text.isEmpty;
  56. });
  57. });
  58. }
  59. @override
  60. void dispose() {
  61. _subscription?.cancel();
  62. widget.controller?.removeListener(() {});
  63. widget.controller?.dispose();
  64. super.dispose();
  65. }
  66. Future _getVCode() async {
  67. bool isSuccess = await widget.getVCode();
  68. if (isSuccess != null && isSuccess) {
  69. setState(() {
  70. s = second;
  71. _isClick = false;
  72. });
  73. _subscription = Observable.periodic(Duration(seconds: 1), (i) => i)
  74. .take(second)
  75. .listen((i) {
  76. setState(() {
  77. s = second - i - 1;
  78. _isClick = s < 1;
  79. });
  80. });
  81. }
  82. }
  83. @override
  84. Widget build(BuildContext context) {
  85. if (widget.config != null && defaultTargetPlatform == TargetPlatform.iOS) {
  86. // 因Android平台输入法兼容问题,所以只配置IOS平台
  87. FormKeyboardActions.setKeyboardActions(context, widget.config);
  88. }
  89. ThemeData themeData = Theme.of(context);
  90. bool isDark = themeData.brightness == Brightness.dark;
  91. return Stack(
  92. alignment: Alignment.centerRight,
  93. children: <Widget>[
  94. TextField(
  95. style: TextStyle(fontSize: 20),
  96. onSubmitted: (v){
  97. FocusScope.of(context).requestFocus(widget.focusNode);
  98. },
  99. focusNode: widget.focusNode,
  100. maxLength: widget.maxLength,
  101. obscureText: widget.isInputPwd ? !_isShowPwd : false,
  102. autofocus: widget.autoFocus,
  103. controller: widget.controller,
  104. textInputAction: TextInputAction.done,
  105. keyboardType: widget.keyboardType,
  106. // 数字、手机号限制格式为0到9(白名单), 密码限制不包含汉字(黑名单)
  107. inputFormatters: (widget.keyboardType == TextInputType.number ||
  108. widget.keyboardType == TextInputType.phone)
  109. ? [WhitelistingTextInputFormatter(RegExp("[0-9]"))]
  110. : [BlacklistingTextInputFormatter(RegExp("[\u4e00-\u9fa5]"))],
  111. decoration: InputDecoration(
  112. contentPadding: const EdgeInsets.symmetric(vertical: 16.0),
  113. hintText: widget.hintText,
  114. hintStyle: TextStyle(
  115. fontSize: Dimens.font_sp14,
  116. fontWeight: FontWeight.normal,
  117. color: Colours.text_gray_c
  118. ),
  119. counterText: "",
  120. focusedBorder: UnderlineInputBorder(
  121. borderSide:
  122. BorderSide(color: Colours.text_gray_c, width: 0.8)),
  123. enabledBorder: UnderlineInputBorder(
  124. borderSide: BorderSide(
  125. color: Theme.of(context).dividerTheme.color,
  126. width: 0.8))),
  127. ),
  128. Row(
  129. mainAxisSize: MainAxisSize.min,
  130. children: <Widget>[
  131. _isShowDelete
  132. ? Gaps.empty
  133. : GestureDetector(
  134. child: LoadAssetImage(
  135. "login/icon_delete",
  136. key: Key('${widget.keyName}_delete'),
  137. width: 18.0,
  138. height: 18.0,
  139. ),
  140. onTap: () => widget.controller.text = "",
  141. ),
  142. !widget.isInputPwd ? Gaps.empty : Gaps.hGap15,
  143. !widget.isInputPwd
  144. ? Gaps.empty
  145. : GestureDetector(
  146. child: LoadAssetImage(
  147. _isShowPwd ? "login/icon_display" : "login/icon_hide",
  148. key: Key('${widget.keyName}_showPwd'),
  149. width: 18.0,
  150. height: 18.0,
  151. ),
  152. onTap: () {
  153. setState(() {
  154. _isShowPwd = !_isShowPwd;
  155. });
  156. },
  157. ),
  158. widget.getVCode == null ? Gaps.empty : Gaps.hGap15,
  159. widget.getVCode == null
  160. ? Gaps.empty
  161. : Theme(
  162. data: Theme.of(context).copyWith(
  163. buttonTheme: ButtonThemeData(
  164. padding: const EdgeInsets.symmetric(horizontal: 8.0),
  165. height: 26.0,
  166. minWidth: 76.0,
  167. materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
  168. ),
  169. ),
  170. child: FlatButton(
  171. onPressed: _isClick ? _getVCode : null,
  172. textColor: Colors.white,
  173. color: Colours.app_main,
  174. disabledTextColor:
  175. isDark ? Colours.dark_text : Colors.white,
  176. disabledColor:
  177. isDark ? Colours.dark_text_gray : Colours.text_gray_c,
  178. shape: RoundedRectangleBorder(
  179. borderRadius: BorderRadius.circular(22.0),
  180. side: BorderSide(
  181. color: _isClick
  182. ? themeData.primaryColor
  183. : Colors.transparent,
  184. width: 0.8,
  185. )),
  186. child: Text(
  187. _isClick ? "获取验证码" : "($s s)",
  188. style: TextStyle(fontSize: Dimens.font_sp12),
  189. ),
  190. ),
  191. )
  192. ],
  193. )
  194. ],
  195. );
  196. }
  197. }