Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

318 linhas
10 KiB

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart';
  3. import 'package:flutter_easyloading/flutter_easyloading.dart';
  4. import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
  5. import 'package:gap/gap.dart';
  6. import 'package:get/get.dart';
  7. import 'package:demo001/tools/color_utils.dart';
  8. import 'package:demo001/tools/textStyle.dart';
  9. import 'package:demo001/tools/widgets.dart';
  10. import 'login_logic.dart';
  11. import 'login_state.dart';
  12. /// @description:
  13. /// @author
  14. /// @date: 2025-01-11 17:25:10
  15. class LoginScene extends StatelessWidget {
  16. final LoginLogic logic = Get.put(LoginLogic());
  17. final LoginState state = Get.find<LoginLogic>().state;
  18. LoginScene({Key? key}) : super(key: key);
  19. double width = 0;
  20. double height = 0;
  21. @override
  22. Widget build(BuildContext context) {
  23. width = MediaQuery.of(context).size.width;
  24. height = MediaQuery.of(context).size.height;
  25. return Scaffold(
  26. backgroundColor: Color.fromARGB(255, 18, 19, 24),
  27. body: KeyboardDismissOnTap(
  28. child: Container(
  29. height: height,
  30. padding: EdgeInsets.all(8),
  31. child: SingleChildScrollView(
  32. child: Column(
  33. mainAxisSize: MainAxisSize.max,
  34. children: [
  35. Gap(45),
  36. topWidget,
  37. Gap(50),
  38. logo,
  39. Gap(15),
  40. emailInput,
  41. Gap(15),
  42. pinInput,
  43. Gap(30),
  44. loginBtn,
  45. Gap(80),
  46. otherLogin,
  47. Gap(180),
  48. protocolWidget,
  49. Gap(15),
  50. ],
  51. ),
  52. ),
  53. ),
  54. ),
  55. );
  56. }
  57. Widget get topWidget => Row(
  58. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  59. children: [
  60. Text(
  61. '联系客服',
  62. style: Style.clean,
  63. ),
  64. Text(
  65. '登录/注册账号',
  66. style: Style.navTitle,
  67. ),
  68. Text(
  69. '联系客服',
  70. style: Style.normalWhiteGrey,
  71. )
  72. ],
  73. );
  74. Widget get logo => Container(
  75. child: Icon(
  76. Icons.logo_dev,
  77. size: 100,
  78. color: white,
  79. ),
  80. );
  81. Widget get phoneInput => SizedBox(
  82. width: width * 0.9,
  83. child: Container(
  84. height: 45,
  85. decoration: BoxDecoration(
  86. color: inputBgColor, borderRadius: BorderRadius.circular(8)),
  87. child: Obx(() => TextField(
  88. controller: state.phoneContr,
  89. focusNode: state.phoneNode,
  90. textInputAction: TextInputAction.next,
  91. style: Style.normalBold,
  92. keyboardType: TextInputType.phone,
  93. inputFormatters: state.countryCode.value == '86'
  94. ? [
  95. FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
  96. LengthLimitingTextInputFormatter(11),
  97. ]
  98. : [
  99. FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
  100. ],
  101. decoration: InputDecoration(
  102. border: InputBorder.none,
  103. hintText: '请输入手机号',
  104. contentPadding: EdgeInsets.symmetric(horizontal: 8),
  105. ),
  106. onSubmitted: (value) {
  107. if (value.length != 11) {
  108. EasyLoading.showError('手机号码错误!');
  109. } else {
  110. state.emailNode.nextFocus();
  111. }
  112. },
  113. )),
  114. ),
  115. );
  116. Widget get emailInput => SizedBox(
  117. width: width * 0.9,
  118. child: Container(
  119. height: 45,
  120. decoration: BoxDecoration(
  121. color: inputBgColor, borderRadius: BorderRadius.circular(8)),
  122. child: TextField(
  123. controller: state.emailContr,
  124. focusNode: state.emailNode,
  125. style: Style.normalBold,
  126. textInputAction: TextInputAction.next,
  127. keyboardType: TextInputType.emailAddress,
  128. decoration: InputDecoration(
  129. border: InputBorder.none,
  130. hintText: '请输入邮箱',
  131. contentPadding: EdgeInsets.symmetric(horizontal: 8),
  132. ),
  133. onSubmitted: (value) {
  134. final emailRegex =
  135. RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$');
  136. if (!emailRegex.hasMatch(state.emailContr.text)) {
  137. EasyLoading.showError('邮箱输入错误,请重新输入!');
  138. } else {
  139. state.pinNode.nextFocus();
  140. }
  141. },
  142. ),
  143. ),
  144. );
  145. Widget get pinInput => SizedBox(
  146. width: width * 0.9,
  147. child: Row(
  148. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  149. children: [
  150. Container(
  151. height: 45,
  152. width: width * 0.5,
  153. decoration: BoxDecoration(
  154. color: inputBgColor, borderRadius: BorderRadius.circular(8)),
  155. child: Obx(() => TextField(
  156. controller: state.pinContr,
  157. focusNode: state.pinNode,
  158. style: Style.normalBold,
  159. textInputAction: TextInputAction.send,
  160. keyboardType: TextInputType.phone,
  161. inputFormatters: state.countryCode.value == '86'
  162. ? [
  163. FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
  164. LengthLimitingTextInputFormatter(4),
  165. ]
  166. : [
  167. FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
  168. ],
  169. decoration: InputDecoration(
  170. border: InputBorder.none,
  171. hintText: '请输入验证码',
  172. contentPadding: EdgeInsets.symmetric(horizontal: 8),
  173. ),
  174. onSubmitted: (value) {
  175. if (value.length != 4) {
  176. EasyLoading.showError("验证码错误");
  177. } else {
  178. logic.login();
  179. }
  180. },
  181. )),
  182. ),
  183. GestureDetector(
  184. onTap: () {
  185. if (!isEmail(state.emailContr.text)) {
  186. EasyLoading.showError('邮箱输入错误,请重新输入!');
  187. } else {
  188. logic.getEmailPin();
  189. }
  190. },
  191. child: Container(
  192. height: 45,
  193. width: width * 0.35,
  194. alignment: Alignment.center,
  195. decoration: BoxDecoration(
  196. color: lightBlue, borderRadius: BorderRadius.circular(8)),
  197. child: Obx(() => Text(
  198. state.countDown.value == 60
  199. ? '获取验证码'
  200. : '${state.countDown.value}S后重新获取',
  201. style: Style.normalWhiteGrey,
  202. )),
  203. ),
  204. )
  205. ],
  206. ),
  207. );
  208. Widget get loginBtn => GestureDetector(
  209. onTap: logic.login,
  210. child: Container(
  211. height: 45,
  212. width: width * 0.9,
  213. alignment: Alignment.center,
  214. decoration: BoxDecoration(
  215. color: blue, borderRadius: BorderRadius.circular(8)),
  216. child: Obx(() => Text(
  217. state.isWechatLogin.value ? '确定绑定' : '登录',
  218. style: Style.normalBold,
  219. )),
  220. ),
  221. );
  222. Widget get otherLogin => Container(
  223. child: Column(
  224. children: [
  225. Row(
  226. mainAxisAlignment: MainAxisAlignment.center,
  227. children: [
  228. Container(
  229. color: white.withAlpha(30),
  230. height: 1,
  231. width: width * 0.25,
  232. ),
  233. Text(
  234. ' 其他登录方式 ',
  235. style: Style.normalSmallWhiteGrey,
  236. ),
  237. Container(
  238. color: white.withAlpha(30),
  239. height: 1,
  240. width: width * 0.25,
  241. ),
  242. ],
  243. ),
  244. Gap(15),
  245. GestureDetector(
  246. onTap: () {
  247. state.isWechatLogin.value = !state.isWechatLogin.value;
  248. },
  249. child: Icon(
  250. Icons.wechat,
  251. size: 60,
  252. color: green,
  253. ),
  254. ),
  255. ],
  256. ),
  257. );
  258. Widget get protocolWidget => Row(
  259. mainAxisSize: MainAxisSize.max,
  260. mainAxisAlignment: MainAxisAlignment.center,
  261. children: [
  262. GestureDetector(
  263. onTap: () {
  264. state.protocolCheck.value = !state.protocolCheck.value;
  265. },
  266. child: Row(
  267. children: [
  268. Obx(() => Container(
  269. child: Icon(
  270. state.protocolCheck.value
  271. ? Icons.check_circle
  272. : Icons.radio_button_unchecked,
  273. color: state.protocolCheck.value ? blue : white,
  274. size: 16,
  275. ),
  276. )),
  277. Text(
  278. ' 我已阅读并同意',
  279. style: Style.normalSmall2,
  280. ),
  281. ],
  282. ),
  283. ),
  284. GestureDetector(
  285. onTap: () {},
  286. child: Text(
  287. '《用户服务协议》',
  288. style: Style.normalBlueSmall2,
  289. ),
  290. ),
  291. Text(
  292. '和',
  293. style: Style.normalSmall2,
  294. ),
  295. GestureDetector(
  296. onTap: () {},
  297. child: Text(
  298. '《用户服务协议》',
  299. style: Style.normalBlueSmall2,
  300. ),
  301. )
  302. ],
  303. );
  304. }