login_page.dart 11 KB

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