register.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:eitc_erm_app/utils/Component.dart';
  4. import 'package:eitc_erm_app/utils/Constants.dart';
  5. import 'package:eitc_erm_app/utils/Utils.dart';
  6. import 'package:eitc_erm_app/utils/logger.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:flutter/services.dart';
  9. import 'package:http/http.dart' as http;
  10. import 'package:http/http.dart';
  11. import 'agreement.dart';
  12. import 'bean/normal_response.dart';
  13. void main() {
  14. WidgetsFlutterBinding.ensureInitialized();
  15. runApp(Register());
  16. }
  17. class Register extends StatefulWidget {
  18. @override
  19. State<StatefulWidget> createState() => RegisterState();
  20. }
  21. class RegisterState extends State<Register> {
  22. @override
  23. Widget build(BuildContext context) {
  24. return Scaffold(
  25. appBar: new AppBar(
  26. title: const Text('注册',
  27. style: TextStyle(
  28. color: Colors.white,
  29. )),
  30. centerTitle: true,
  31. elevation: 0.5,
  32. backgroundColor: Global.StatusBarColor,
  33. leading: new IconButton(
  34. tooltip: '返回上一页',
  35. icon: const Icon(
  36. Icons.arrow_back_ios,
  37. color: Colors.white,
  38. ),
  39. onPressed: () {
  40. Navigator.of(context).pop();
  41. //_nextPage(-1);
  42. },
  43. ),
  44. ),
  45. body: Center(
  46. child: MyBody(),
  47. ),
  48. );
  49. }
  50. }
  51. class MyBody extends StatefulWidget {
  52. @override
  53. _MyBodyState createState() => _MyBodyState();
  54. }
  55. class _MyBodyState extends State<MyBody> {
  56. bool isButtonEnable = true; //按钮状态 是否可点击
  57. String buttonText = '发送验证码'; //初始文本
  58. int count = 60; //初始倒计时时间
  59. Timer? timer; //倒计时的计时器
  60. TextEditingController mController = TextEditingController();
  61. TextEditingController phoneController = TextEditingController();
  62. TextEditingController identificationCardController = TextEditingController();
  63. TextEditingController passwordController = TextEditingController();
  64. bool isPasswordLogin = false;
  65. String passText = "验证码";
  66. String loginText = "密码登录";
  67. String hintText = "请输入验证码";
  68. bool _isChecked = false;
  69. void _buttonClickListen() {
  70. setState(() {
  71. if (!Utils.isChinaPhoneLegal(phoneController.text)) {
  72. Component.toast("请输入正确手机号码!", 0);
  73. return null;
  74. }
  75. sendCaptchaCode();
  76. if (isButtonEnable) {
  77. isButtonEnable = false; //按钮状态标记
  78. _initTimer();
  79. return null; //返回null按钮禁止点击
  80. } else {
  81. return null; //返回null按钮禁止点击
  82. }
  83. });
  84. }
  85. void _initTimer() {
  86. timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) {
  87. count--;
  88. setState(() {
  89. if (count == 0) {
  90. timer.cancel(); //倒计时结束取消定时器
  91. isButtonEnable = true; //按钮可点击
  92. count = 60; //重置时间
  93. buttonText = '发送验证码'; //重置按钮文本
  94. } else {
  95. buttonText = '重新发送($count)'; //更新文本内容
  96. }
  97. });
  98. });
  99. }
  100. Future<String?> sendCaptchaCode() async {
  101. var params = {
  102. 'phoneNumber': phoneController.text,
  103. };
  104. var response = await http.post(
  105. Uri.parse('${Global.BaseUrl}sendCaptchaCode'),
  106. body: encodeBody(params),
  107. headers: jsonHeaders());
  108. if (response.statusCode == 200) {
  109. final json = decodeBodyToJson(response.bodyBytes);
  110. logd(json);
  111. NormalResponse mNormalResponse = NormalResponse.fromJson(json);
  112. if (mNormalResponse.code == Global.responseSuccessCode) {
  113. Component.toast("短信发送成功,请查收!", 2);
  114. } else {
  115. Component.toast(mNormalResponse.msg.toString(), 0);
  116. }
  117. return response.toString();
  118. } else {
  119. Component.toast("出错了,请稍后再试!", 0);
  120. return null;
  121. }
  122. }
  123. @override
  124. void dispose() {
  125. timer?.cancel();
  126. super.dispose();
  127. }
  128. @override
  129. Widget build(BuildContext context) {
  130. return Container(
  131. padding: const EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0),
  132. child: Column(
  133. children: <Widget>[
  134. Container(
  135. color: Colors.white,
  136. padding: const EdgeInsets.only(left: 10, right: 10),
  137. child: Column(
  138. children: <Widget>[
  139. Row(
  140. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  141. crossAxisAlignment: CrossAxisAlignment.baseline,
  142. textBaseline: TextBaseline.alphabetic,
  143. children: <Widget>[
  144. const Text(
  145. '身份证',
  146. style: TextStyle(fontSize: 13, color: Color(0xff333333)),
  147. ),
  148. Expanded(
  149. child: Padding(
  150. padding:
  151. const EdgeInsets.only(left: 5, right: 15, top: 5),
  152. child: TextFormField(
  153. maxLines: 1,
  154. onSaved: (value) {},
  155. controller: identificationCardController,
  156. textAlign: TextAlign.left,
  157. inputFormatters: [
  158. FilteringTextInputFormatter.digitsOnly,
  159. LengthLimitingTextInputFormatter(18)
  160. ],
  161. decoration: const InputDecoration(
  162. hintText: ('请填写身份证号码'),
  163. hintStyle: TextStyle(
  164. color: Color(0xff999999),
  165. fontSize: 13,
  166. ),
  167. alignLabelWithHint: true,
  168. border:
  169. OutlineInputBorder(borderSide: BorderSide.none),
  170. ),
  171. ),
  172. ),
  173. ),
  174. ],
  175. ),
  176. Row(
  177. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  178. crossAxisAlignment: CrossAxisAlignment.baseline,
  179. textBaseline: TextBaseline.alphabetic,
  180. children: <Widget>[
  181. const Text(
  182. '密码',
  183. style: TextStyle(fontSize: 13, color: Color(0xff333333)),
  184. ),
  185. Expanded(
  186. child: Padding(
  187. padding:
  188. const EdgeInsets.only(left: 20, right: 15, top: 5),
  189. child: TextFormField(
  190. maxLines: 1,
  191. onSaved: (value) {},
  192. controller: passwordController,
  193. textAlign: TextAlign.left,
  194. inputFormatters: [
  195. FilteringTextInputFormatter.digitsOnly,
  196. LengthLimitingTextInputFormatter(20)
  197. ],
  198. decoration: const InputDecoration(
  199. hintText: ('请填写密码'),
  200. hintStyle: TextStyle(
  201. color: Color(0xff999999),
  202. fontSize: 13,
  203. ),
  204. alignLabelWithHint: true,
  205. border:
  206. OutlineInputBorder(borderSide: BorderSide.none),
  207. ),
  208. ),
  209. ),
  210. ),
  211. ],
  212. ),
  213. Row(
  214. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  215. crossAxisAlignment: CrossAxisAlignment.baseline,
  216. textBaseline: TextBaseline.alphabetic,
  217. children: <Widget>[
  218. const Text(
  219. '+86',
  220. style: TextStyle(fontSize: 13, color: Color(0xff333333)),
  221. ),
  222. Expanded(
  223. child: Padding(
  224. padding:
  225. const EdgeInsets.only(left: 20, right: 15, top: 5),
  226. child: TextFormField(
  227. maxLines: 1,
  228. onSaved: (value) {},
  229. controller: phoneController,
  230. textAlign: TextAlign.left,
  231. inputFormatters: [
  232. FilteringTextInputFormatter.digitsOnly,
  233. LengthLimitingTextInputFormatter(11)
  234. ],
  235. decoration: const InputDecoration(
  236. hintText: ('请填写手机号'),
  237. hintStyle: TextStyle(
  238. color: Color(0xff999999),
  239. fontSize: 13,
  240. ),
  241. alignLabelWithHint: true,
  242. border:
  243. OutlineInputBorder(borderSide: BorderSide.none),
  244. ),
  245. ),
  246. ),
  247. ),
  248. ],
  249. ),
  250. Row(
  251. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  252. crossAxisAlignment: CrossAxisAlignment.baseline,
  253. textBaseline: TextBaseline.alphabetic,
  254. children: <Widget>[
  255. Text(
  256. passText,
  257. style: const TextStyle(
  258. fontSize: 13, color: Color(0xff333333)),
  259. ),
  260. Expanded(
  261. child: Padding(
  262. padding:
  263. const EdgeInsets.only(left: 15, right: 15, top: 0),
  264. child: TextFormField(
  265. maxLines: 1,
  266. onSaved: (value) {},
  267. controller: mController,
  268. textAlign: TextAlign.left,
  269. inputFormatters: [
  270. FilteringTextInputFormatter.digitsOnly,
  271. LengthLimitingTextInputFormatter(6)
  272. ],
  273. decoration: const InputDecoration(
  274. hintText: ('请输入验证码'),
  275. contentPadding: EdgeInsets.only(top: -5, bottom: 0),
  276. hintStyle: TextStyle(
  277. color: Color(0xff999999),
  278. fontSize: 13,
  279. ),
  280. alignLabelWithHint: true,
  281. border:
  282. OutlineInputBorder(borderSide: BorderSide.none),
  283. ),
  284. ),
  285. ),
  286. ),
  287. Container(
  288. padding: const EdgeInsets.all(0),
  289. width: 120,
  290. child: Visibility(
  291. visible: !isPasswordLogin,
  292. child: ElevatedButton(
  293. style: ButtonStyle(
  294. backgroundColor:
  295. WidgetStateProperty.resolveWith<Color>(
  296. (states) {
  297. if (states.contains(WidgetState.disabled)) {
  298. return Colors.brown;
  299. }
  300. return Colors.blue;
  301. }),
  302. foregroundColor:
  303. const WidgetStatePropertyAll(Colors.white),
  304. ),
  305. onPressed: () {
  306. setState(() {
  307. _buttonClickListen();
  308. });
  309. },
  310. child: Text(
  311. buttonText,
  312. style: const TextStyle(
  313. fontSize: 12,
  314. ),
  315. ),
  316. ),
  317. ),
  318. ),
  319. ],
  320. ),
  321. Padding(
  322. padding: const EdgeInsets.only(top: 20, bottom: 0),
  323. child: Row(
  324. mainAxisAlignment: MainAxisAlignment.start,
  325. children: <Widget>[
  326. SizedBox(
  327. width: 20.0,
  328. height: 20.0,
  329. child: Checkbox(
  330. value: _isChecked,
  331. onChanged: (bool? newValue) {
  332. setState(() {
  333. _isChecked = newValue ?? false;
  334. });
  335. },
  336. activeColor: Colors.green, // 选中时的颜色
  337. checkColor: Colors.white, // 选中标记的颜色
  338. ),
  339. ),
  340. const Text(
  341. " 同意",
  342. style: TextStyle(
  343. fontSize: 13, color: Color(0xff333333)),
  344. ),
  345. GestureDetector(
  346. onTap: () {
  347. Navigator.push(
  348. context,
  349. MaterialPageRoute(
  350. builder: (context) => Agreement()),
  351. );
  352. },
  353. child: const Text(
  354. "口腔医院相关服务协议及隐私政策",
  355. style: TextStyle(
  356. fontSize: 14, color: Colors.blueAccent),
  357. ),
  358. ),
  359. ]))
  360. ],
  361. ),
  362. ),
  363. Container(
  364. width: double.infinity,
  365. height: 45,
  366. margin: const EdgeInsets.only(top: 20, left: 10, right: 10),
  367. child: ElevatedButton(
  368. style: ButtonStyle(
  369. backgroundColor:
  370. WidgetStateProperty.resolveWith<Color>((states) {
  371. return Colors.blue; // Regular color
  372. }),
  373. ),
  374. onPressed: () {
  375. logd('手机号:${phoneController.text},验证码:${mController.text}');
  376. register();
  377. },
  378. child: Text(
  379. '注册',
  380. style: TextStyle(color: Colors.white, fontSize: 15),
  381. ),
  382. ),
  383. ),
  384. ],
  385. ),
  386. );
  387. }
  388. void _showDialog() {
  389. showDialog(
  390. context: context,
  391. builder: (context) {
  392. return AlertDialog(
  393. title: const Text('提示'),
  394. content: const Text('请您阅读《口腔医院相关服务协议及隐私政策》并勾选同意'),
  395. actions: <Widget>[
  396. TextButton(
  397. onPressed: () {
  398. Navigator.of(context).pop();
  399. },
  400. child: const Text("确定",
  401. style: TextStyle(color: Colors.white, fontSize: 13)),
  402. ),
  403. ],
  404. );
  405. },
  406. );
  407. }
  408. Future<String?> register() async {
  409. if (phoneController.text.length < 11) {
  410. Component.toast("请正确填写电话号码!", 0);
  411. return null;
  412. } else if (mController.text.length < 6) {
  413. Component.toast("请正确填写验证码!", 0);
  414. return null;
  415. } else if (!Utils.validateIDCard(identificationCardController.text)) {
  416. Component.toast("请正确填写身份证号码!", 0);
  417. return null;
  418. } else if (passwordController.text.length < 6) {
  419. Component.toast("请正确填写密码!", 0);
  420. return null;
  421. } else if (!Utils.isChinaPhoneLegal(phoneController.text)) {
  422. Component.toast("请输入正确手机号码!", 0);
  423. return null;
  424. } else if (!Utils.isVerifyCode(mController.text)) {
  425. Component.toast("请输入正确验证码!", 0);
  426. return null;
  427. }
  428. if (!_isChecked) {
  429. _showDialog();
  430. return null;
  431. }
  432. bool bo = await _checkCaptchaCode();
  433. if (!bo) {
  434. Component.toast("验证码错误!", 0);
  435. return null;
  436. }
  437. var params = {
  438. 'phoneNumber': phoneController.text,
  439. 'captchaCode': mController.text,
  440. 'identificationCard': identificationCardController.text,
  441. 'password': passwordController.text,
  442. };
  443. var response = await http.post(Uri.parse('${Global.BaseUrl}register'),
  444. body: encodeBody(params), headers: jsonHeaders());
  445. if (response.statusCode == 200) {
  446. final json = decodeBodyToJson(response.bodyBytes);
  447. logd(json);
  448. NormalResponse mNormalResponse = new NormalResponse.fromJson(json);
  449. if (mNormalResponse.code == Global.responseSuccessCode) {
  450. Component.toast("注册成功!", 2);
  451. Navigator.pushReplacementNamed(context, "/login");
  452. } else {
  453. Component.toast(mNormalResponse.msg.toString(), 0);
  454. }
  455. return response.toString();
  456. } else {
  457. Component.toast("出错了,请稍后再试!", 0);
  458. return null;
  459. }
  460. }
  461. ///检查验证码
  462. Future<bool> _checkCaptchaCode() async {
  463. Map<String, dynamic> params = {
  464. "phoneNumber": phoneController.text,
  465. "captchaCode": mController.text
  466. };
  467. Uri uri = Uri.parse("${Global.BaseUrl}api/checkCaptchaCode");
  468. logd("检查验证码,uri=$uri");
  469. Response response =
  470. await http.post(uri, body: encodeBody(params), headers: jsonHeaders());
  471. if (response.statusCode == 200) {
  472. final json = decodeBodyToJson(response.bodyBytes);
  473. logd("检查验证码结果$json");
  474. NormalResponse mNormalResponse = NormalResponse.fromJson(json);
  475. return mNormalResponse.code == 200;
  476. } else {
  477. logd("检查验证码异常${response.body}");
  478. }
  479. return false;
  480. }
  481. }