change_pwd_page.dart 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
  5. import 'package:lszlgl/base/base_state.dart';
  6. import 'package:lszlgl/main.dart';
  7. import 'package:lszlgl/network/my_api.dart';
  8. import 'package:lszlgl/widget/button.dart';
  9. import 'package:lszlgl/widget/password_textfield.dart';
  10. /// 修改密码
  11. class ChangePwdPage extends StatefulWidget {
  12. final bool startHome;
  13. const ChangePwdPage({
  14. Key? key,
  15. bool? startHome,
  16. }) : startHome = startHome ?? false,
  17. super(key: key);
  18. @override
  19. State<ChangePwdPage> createState() => _ChangePwdPageState();
  20. }
  21. class _ChangePwdPageState extends BaseState<ChangePwdPage> {
  22. late TextEditingController oldPwdCtrl;
  23. late TextEditingController newPwdCtrl;
  24. late TextEditingController confirmPwdCtrl;
  25. final ValueNotifier<String?> oldError = null.notifier();
  26. final ValueNotifier<String?> newError = null.notifier();
  27. final ValueNotifier<String?> confirmError = null.notifier();
  28. void onChange() async {
  29. List<bool> verifyList = await Future.wait([verifyOldText(), verifyNewText(), verifyConfirmText()]);
  30. if (!verifyList.firstWhere((element) => !element, orElse: () => true)) {
  31. return;
  32. }
  33. MyNavigator.showLoading(msg: '密码修改中...');
  34. try {
  35. var rsp = await MyApi.get().updatePassword({
  36. 'oldPassword': oldPwdCtrl.text,
  37. 'newPassword': newPwdCtrl.text,
  38. });
  39. if (rsp.data ?? false) {
  40. MyNavigator.showToast('修改成功');
  41. if (widget.startHome) {
  42. // 进入主页
  43. MyRouter.startMain(popAll: true);
  44. } else {
  45. MyNavigator.pop();
  46. }
  47. }
  48. } catch (e) {
  49. logger.e(e);
  50. }
  51. MyNavigator.dismissLoading();
  52. }
  53. Future<bool> verifyOldText() async {
  54. var text = oldPwdCtrl.text;
  55. if (text.isEmpty) {
  56. oldError.value = '请输入旧密码';
  57. return false;
  58. }
  59. int length = text.length;
  60. if (length < 8) {
  61. oldError.value = '密码长度要大于8位';
  62. return false;
  63. }
  64. oldError.value = null;
  65. return true;
  66. }
  67. Future<bool> verifyNewText() async {
  68. var text = newPwdCtrl.text;
  69. if (text.isEmpty) {
  70. newError.value = '请输入新密码';
  71. return false;
  72. }
  73. int length = text.length;
  74. if (length < 8) {
  75. newError.value = '密码长度要大于8位';
  76. return false;
  77. }
  78. var regex = RegExp(r'\d');
  79. if (!regex.hasMatch(text)) {
  80. newError.value = '密码要包含数字';
  81. return false;
  82. }
  83. regex = RegExp(r'[a-zA-Z]');
  84. if (!regex.hasMatch(text)) {
  85. newError.value = '密码要包含大写字母或小写字母';
  86. return false;
  87. }
  88. regex = RegExp(r'[!\"#$%&()*+,-./:;<=>?@\]\[^_`{|}~]');
  89. if (!regex.hasMatch(text)) {
  90. newError.value = '密码要包含特殊字符';
  91. return false;
  92. }
  93. newError.value = null;
  94. return true;
  95. }
  96. Future<bool> verifyConfirmText() async {
  97. var text = confirmPwdCtrl.text;
  98. if (text.isEmpty) {
  99. confirmError.value = '请输入确认密码';
  100. return false;
  101. }
  102. if (text != newPwdCtrl.text) {
  103. confirmError.value = '两次输入密码不一致';
  104. return false;
  105. }
  106. confirmError.value = null;
  107. return true;
  108. }
  109. @override
  110. void initState() {
  111. super.initState();
  112. oldPwdCtrl = TextEditingController();
  113. newPwdCtrl = TextEditingController();
  114. confirmPwdCtrl = TextEditingController();
  115. }
  116. @override
  117. void dispose() {
  118. super.dispose();
  119. }
  120. @override
  121. Widget build(BuildContext context) {
  122. return myScaffold(
  123. child: KeyboardDismissOnTap(
  124. dismissOnCapturedTaps: true,
  125. child: buildBody(),
  126. ),
  127. );
  128. }
  129. Widget buildBody() {
  130. return Column(
  131. children: [
  132. myAppBar(title: '修改密码'),
  133. Expanded(
  134. child: SingleChildScrollView(
  135. child: Column(
  136. children: [
  137. buildList(),
  138. const SizedBox(height: 32),
  139. MyButton(
  140. '确认修改',
  141. onTap: onChange,
  142. gradient: const LinearGradient(colors: [Color(0xFF3BD2E5), Color(0xFF247AF8)]),
  143. alignment: Alignment.center,
  144. minHeight: 40,
  145. margin: const EdgeInsets.symmetric(horizontal: 24),
  146. ),
  147. ],
  148. ),
  149. ),
  150. ),
  151. ],
  152. );
  153. }
  154. Widget buildList() {
  155. var formatters = [FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9!\"#$%&()*+,-./:;<=>?@\]\[^_`{|}~]'))];
  156. return Container(
  157. margin: const EdgeInsets.symmetric(horizontal: 12),
  158. padding: const EdgeInsets.fromLTRB(16, 24, 16, 8),
  159. clipBehavior: Clip.hardEdge,
  160. decoration: const BoxDecoration(
  161. borderRadius: BorderRadius.all(Radius.circular(10)),
  162. color: Colors.white,
  163. ),
  164. child: Column(
  165. children: [
  166. oldError.builder(
  167. (error) => buildEdit(
  168. ctrl: oldPwdCtrl,
  169. labelText: '旧密码',
  170. hint: '请输入',
  171. formatters: formatters,
  172. inputType: TextInputType.visiblePassword,
  173. obscureText: true,
  174. errorText: error,
  175. onChanged: (value) => verifyOldText(),
  176. ),
  177. ),
  178. const SizedBox(height: 12),
  179. newError.builder(
  180. (error) => buildEdit(
  181. ctrl: newPwdCtrl,
  182. labelText: '新密码',
  183. hint: '密码长度8-20位,需包含英文、数字及字符',
  184. formatters: formatters,
  185. inputType: TextInputType.visiblePassword,
  186. obscureText: true,
  187. maxLength: 20,
  188. onChanged: (value) => verifyNewText(),
  189. errorText: error,
  190. ),
  191. ),
  192. const SizedBox(height: 12),
  193. confirmError.builder(
  194. (error) => buildEdit(
  195. ctrl: confirmPwdCtrl,
  196. labelText: '确认密码',
  197. hint: '密码长度8-20位,需包含英文、数字及字符',
  198. formatters: formatters,
  199. inputType: TextInputType.visiblePassword,
  200. obscureText: true,
  201. maxLength: 20,
  202. onChanged: (value) => verifyConfirmText(),
  203. errorText: error,
  204. ),
  205. ),
  206. ],
  207. ),
  208. );
  209. }
  210. Widget buildEdit({
  211. required TextEditingController ctrl,
  212. String? hint,
  213. String? labelText,
  214. TextInputAction action = TextInputAction.next,
  215. bool obscureText = false,
  216. ValueChanged? onSubmit,
  217. ValueChanged<String>? onChanged,
  218. List<TextInputFormatter>? formatters,
  219. int? maxLength,
  220. String? errorText,
  221. TextInputType? inputType,
  222. }) {
  223. return PasswordTextField(
  224. controller: ctrl,
  225. decoration: InputDecoration(
  226. prefixIcon: Padding(
  227. padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
  228. child: Image.asset(imgLoginPwd, height: 18),
  229. ),
  230. prefixIconConstraints: const BoxConstraints(),
  231. border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(100))),
  232. enabledBorder: const OutlineInputBorder(
  233. borderSide: BorderSide(color: Color(0xFFE6E6E6), width: 1),
  234. borderRadius: BorderRadius.all(Radius.circular(100)),
  235. ),
  236. labelText: labelText,
  237. hintText: hint,
  238. hintStyle: const TextStyle(color: Color(0xFFBBBBBB)),
  239. isDense: true,
  240. contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
  241. errorText: errorText,
  242. ),
  243. maxLength: maxLength,
  244. style: const TextStyle(fontSize: 14),
  245. textInputAction: action,
  246. initialObscurity: obscureText,
  247. onSubmitted: onSubmit,
  248. onChanged: onChanged,
  249. inputFormatters: formatters,
  250. );
  251. }
  252. }