login_page.dart 11 KB

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