login_page.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:flutter/src/widgets/framework.dart';
  5. import 'package:flutter_screenutil/flutter_screenutil.dart';
  6. import 'package:keyboard/base/base_page.dart';
  7. import 'package:keyboard/module/login/login_controller.dart';
  8. import 'package:get/get.dart';
  9. import 'package:keyboard/router/app_pages.dart';
  10. import 'package:keyboard/utils/common_expand.dart';
  11. import 'package:keyboard/widget/platform_util.dart';
  12. import '../../data/consts/web_url.dart';
  13. import '../../resource/assets.gen.dart';
  14. import '../../resource/colors.gen.dart';
  15. import '../../resource/string.gen.dart';
  16. import '../../utils/styles.dart';
  17. import '../../widget/click_text_span.dart';
  18. class LoginPage extends BasePage<LoginController> {
  19. const LoginPage({super.key});
  20. static start() {
  21. Get.toNamed(RoutePath.login);
  22. }
  23. @override
  24. Color backgroundColor() {
  25. return const Color(0xFFF6F5FA);
  26. }
  27. @override
  28. immersive() {
  29. return true;
  30. }
  31. @override
  32. Widget buildBody(BuildContext context) {
  33. return PopScope(
  34. canPop: false,
  35. onPopInvokedWithResult: (didPop, result) async {
  36. if (didPop) {
  37. return;
  38. }
  39. controller.clickBack();
  40. },
  41. child: Stack(
  42. children: [
  43. Assets.images.bgLogin.image(fit: BoxFit.contain, width: 360.w),
  44. _buildTitle(),
  45. SafeArea(
  46. child: Column(
  47. children: [
  48. SizedBox(height: 225.w),
  49. buildPhoneTextFiled(),
  50. SizedBox(height: 16.w),
  51. buildCodeTextFiled(),
  52. SizedBox(height: 46.w),
  53. buildLoginButton(),
  54. SizedBox(height: 25.w),
  55. _buildPrivacy(),
  56. Spacer(),
  57. buildOtherLogin(),
  58. SizedBox(height: 20.w),
  59. ],
  60. ),
  61. ),
  62. ],
  63. ),
  64. );
  65. }
  66. _buildTitle() {
  67. return SafeArea(
  68. child: Container(
  69. alignment: Alignment.topLeft,
  70. padding: EdgeInsets.only(top: 16.h, left: 16.w, right: 16.w),
  71. child: Row(
  72. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  73. children: [
  74. GestureDetector(
  75. onTap: controller.clickBack,
  76. child: Assets.images.iconStoreBack.image(
  77. width: 32.w,
  78. height: 32.w,
  79. ),
  80. ),
  81. ],
  82. ),
  83. ),
  84. );
  85. }
  86. Widget buildPhoneTextFiled() {
  87. return Container(
  88. height: 48.w,
  89. margin: EdgeInsets.symmetric(horizontal: 20.w),
  90. padding: EdgeInsets.symmetric(horizontal: 16.w),
  91. decoration: BoxDecoration(
  92. color: Colors.white,
  93. borderRadius: BorderRadius.circular(100.w),
  94. ),
  95. child: Row(
  96. children: [
  97. Expanded(
  98. child: TextField(
  99. cursorHeight: 20.w,
  100. style: TextStyle(
  101. fontSize: 14.sp,
  102. color: Colors.black.withAlpha(204),
  103. fontWeight: FontWeight.w500,
  104. ),
  105. maxLines: 1,
  106. maxLength: 11,
  107. keyboardType: TextInputType.phone,
  108. inputFormatters: [FilteringTextInputFormatter.digitsOnly],
  109. textAlignVertical: TextAlignVertical.center,
  110. textInputAction: TextInputAction.next,
  111. decoration: InputDecoration(
  112. hintText: StringName.loginEtPhoneHint,
  113. counterText: '',
  114. hintStyle: TextStyle(
  115. fontSize: 14.sp,
  116. color: Colors.black.withAlpha(61),
  117. fontWeight: FontWeight.normal,
  118. ),
  119. labelStyle: TextStyle(
  120. fontSize: 14.sp,
  121. color: ColorName.primaryTextColor,
  122. ),
  123. contentPadding: const EdgeInsets.all(0),
  124. border: const OutlineInputBorder(borderSide: BorderSide.none),
  125. enabled: true,
  126. ),
  127. onChanged: controller.onPhoneChanged,
  128. ),
  129. ),
  130. ],
  131. ),
  132. );
  133. }
  134. Widget buildCodeTextFiled() {
  135. return Container(
  136. height: 48.w,
  137. margin: EdgeInsets.symmetric(horizontal: 20.w),
  138. padding: EdgeInsets.symmetric(horizontal: 16.w),
  139. decoration: BoxDecoration(
  140. color: Colors.white,
  141. borderRadius: BorderRadius.circular(100.w),
  142. ),
  143. child: Row(
  144. children: [
  145. Expanded(
  146. child: TextField(
  147. cursorHeight: 20.w,
  148. style: TextStyle(
  149. fontSize: 14.sp,
  150. color: Colors.black.withAlpha(204),
  151. fontWeight: FontWeight.w500,
  152. ),
  153. maxLines: 1,
  154. maxLength: 4,
  155. keyboardType: TextInputType.phone,
  156. inputFormatters: [FilteringTextInputFormatter.digitsOnly],
  157. textAlignVertical: TextAlignVertical.center,
  158. textInputAction: TextInputAction.next,
  159. decoration: InputDecoration(
  160. hintText: StringName.loginPrintVerificationCode,
  161. counterText: '',
  162. hintStyle: TextStyle(
  163. fontSize: 14.sp,
  164. color: Colors.black.withAlpha(61),
  165. fontWeight: FontWeight.normal,
  166. ),
  167. labelStyle: TextStyle(
  168. fontSize: 14.sp,
  169. color: ColorName.primaryTextColor,
  170. ),
  171. contentPadding: const EdgeInsets.all(0),
  172. border: const OutlineInputBorder(borderSide: BorderSide.none),
  173. enabled: true,
  174. ),
  175. onChanged: controller.onCodeChanged,
  176. ),
  177. ),
  178. buildVerificationCodeSendBtn(),
  179. ],
  180. ),
  181. );
  182. }
  183. Widget buildVerificationCodeSendBtn() {
  184. return GestureDetector(
  185. onTap: controller.onSendVerificationCode,
  186. child: Container(
  187. margin: EdgeInsets.only(left: 12.w, right: 8.w),
  188. child: Obx(() {
  189. String txt = "";
  190. if (controller.countDown != null) {
  191. txt =
  192. '${controller.countDown}${StringName.loginRetransmissionCode}';
  193. } else {
  194. if (controller.isFirstSend) {
  195. txt = StringName.loginSendVerificationCode;
  196. } else {
  197. txt = StringName.loginResendCode;
  198. }
  199. }
  200. return Text(
  201. txt,
  202. style: TextStyle(
  203. fontSize: 14.sp,
  204. color:
  205. controller.isFirstSend
  206. ? Colors.black.withAlpha(204)
  207. : Color(0xCC7D46FC),
  208. ),
  209. );
  210. }),
  211. ),
  212. );
  213. }
  214. Widget buildLoginButton() {
  215. return GestureDetector(
  216. onTap: () {
  217. controller.onLoginClick();
  218. },
  219. child: Container(
  220. height: 48.w,
  221. margin: EdgeInsets.symmetric(horizontal: 24.w),
  222. child: Row(
  223. children: [
  224. Expanded(
  225. child: Obx(() {
  226. return Container(
  227. height: 48.w,
  228. decoration:
  229. controller.phone.length == 11 &&
  230. controller.code.isNotEmpty &&
  231. controller.isAgree
  232. ? Styles.getActivateButtonDecoration(50.r)
  233. : Styles.getInactiveButtonDecoration(50.r),
  234. child: Center(
  235. child: Text(
  236. StringName.login,
  237. style: TextStyle(color: Colors.white, fontSize: 16.sp),
  238. ),
  239. ),
  240. );
  241. }),
  242. ),
  243. ],
  244. ),
  245. ),
  246. );
  247. }
  248. Widget _buildPrivacy() {
  249. return Row(
  250. mainAxisAlignment: MainAxisAlignment.center,
  251. children: [
  252. Obx(() {
  253. return GestureDetector(
  254. behavior: HitTestBehavior.opaque,
  255. onTap: () {
  256. controller.clickAgree();
  257. },
  258. child: Padding(
  259. padding: EdgeInsets.symmetric(vertical: 20.w, horizontal: 20.w),
  260. child:
  261. controller.isAgree
  262. ? Assets.images.iconLoginAgreePrivacy.image(
  263. width: 14.w,
  264. height: 14.w,
  265. )
  266. : Container(
  267. width: 14.w,
  268. height: 14.w,
  269. child: Container(
  270. decoration: BoxDecoration(
  271. shape: BoxShape.circle,
  272. border: Border.all(
  273. color: Colors.black.withAlpha(153),
  274. width: 1.w,
  275. ),
  276. ),
  277. ),
  278. ),
  279. ),
  280. );
  281. }),
  282. Transform.translate(
  283. offset: Offset(-17.w, 0),
  284. child: Text.rich(
  285. TextSpan(
  286. children: [
  287. TextSpan(
  288. text: StringName.textSpanIHaveReadAndAgree,
  289. style: TextStyle(
  290. color: Colors.black.withAlpha(128),
  291. fontSize: 12.sp,
  292. fontWeight: FontWeight.w400,
  293. ),
  294. ),
  295. ClickTextSpan(
  296. text: StringName.textSpanPrivacyPolicy,
  297. url: WebUrl.privacyPolicy,
  298. fontSize: 12.sp,
  299. color: Colors.black.withAlpha(204),
  300. ),
  301. TextSpan(
  302. text: StringName.textSpanAnd,
  303. style: TextStyle(
  304. color: Colors.black.withAlpha(128),
  305. fontSize: 12.sp,
  306. fontWeight: FontWeight.w400,
  307. ),
  308. ),
  309. ClickTextSpan(
  310. text: StringName.textSpanServiceTerms,
  311. url: WebUrl.serviceAgreement,
  312. color: Colors.black.withAlpha(204),
  313. fontSize: 12.sp,
  314. ),
  315. ],
  316. ),
  317. ),
  318. ),
  319. ],
  320. );
  321. }
  322. Widget buildOtherLogin() {
  323. return Container(
  324. child: Column(
  325. children: [
  326. Text(
  327. StringName.loginOtherlogin,
  328. style: TextStyle(
  329. color: Colors.black.withValues(alpha: 0.5),
  330. fontSize: 12.sp,
  331. height: 0,
  332. ),
  333. ),
  334. SizedBox(height: 18.h),
  335. Row(
  336. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  337. children: [
  338. Column(
  339. children: [
  340. Material(
  341. color: Colors.white,
  342. shape: const CircleBorder(),
  343. child: InkWell(
  344. customBorder: const CircleBorder(), // 保证点击区域是圆的
  345. onTap: () {
  346. controller.clickAppleLogin();
  347. },
  348. child: SizedBox(
  349. width: 44.w,
  350. height: 44.w,
  351. child: Center(
  352. child: Assets.images.iconLoginDialogApple.image(
  353. width: 22.w,
  354. height: 22.w,
  355. ),
  356. ),
  357. ),
  358. ),
  359. ),
  360. SizedBox(height: 6.w),
  361. Text(
  362. StringName.loginApple,
  363. textAlign: TextAlign.center,
  364. style: TextStyle(
  365. color: Colors.black.withValues(alpha: 0.5),
  366. fontSize: 12.sp,
  367. height: 0,
  368. ),
  369. ),
  370. ],
  371. ),
  372. Column(
  373. children: [
  374. Material(
  375. color: Colors.white,
  376. shape: const CircleBorder(),
  377. child: InkWell(
  378. customBorder: const CircleBorder(), // 保证点击区域是圆的
  379. onTap: () {
  380. controller.clickWxLogin();
  381. },
  382. child: SizedBox(
  383. width: 44.w,
  384. height: 44.w,
  385. child: Center(
  386. child: Assets.images.iconWechatLogoBlack.image(
  387. width: 22.w,
  388. height: 22.w,
  389. ),
  390. ),
  391. ),
  392. ),
  393. ),
  394. SizedBox(height: 6.w),
  395. Text(
  396. StringName.wechat,
  397. textAlign: TextAlign.center,
  398. style: TextStyle(
  399. color: Colors.black.withValues(alpha: 0.5),
  400. fontSize: 12.sp,
  401. height: 0,
  402. ),
  403. ),
  404. ],
  405. ),
  406. ],
  407. ),
  408. ],
  409. ),
  410. );
  411. }
  412. }