|
|
@@ -1,10 +1,19 @@
|
|
|
-
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
import 'package:flutter/src/widgets/framework.dart';
|
|
|
+import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
|
import 'package:keyboard/base/base_page.dart';
|
|
|
import 'package:keyboard/module/login/login_controller.dart';
|
|
|
import 'package:get/get.dart';
|
|
|
import 'package:keyboard/router/app_pages.dart';
|
|
|
+import 'package:keyboard/utils/common_expand.dart';
|
|
|
+
|
|
|
+import '../../data/consts/web_url.dart';
|
|
|
+import '../../resource/assets.gen.dart';
|
|
|
+import '../../resource/colors.gen.dart';
|
|
|
+import '../../resource/string.gen.dart';
|
|
|
+import '../../utils/styles.dart';
|
|
|
+import '../../widget/click_text_span.dart';
|
|
|
|
|
|
class LoginPage extends BasePage<LoginController> {
|
|
|
const LoginPage({super.key});
|
|
|
@@ -14,9 +23,326 @@ class LoginPage extends BasePage<LoginController> {
|
|
|
}
|
|
|
|
|
|
@override
|
|
|
+ Color backgroundColor() {
|
|
|
+ return const Color(0xFFF6F5FA);
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ immersive() {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
Widget buildBody(BuildContext context) {
|
|
|
+ return Stack(
|
|
|
+ children: [
|
|
|
+ Assets.images.bgLogin.image(fit: BoxFit.contain, width: 360.w),
|
|
|
+ SafeArea(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ SizedBox(height: 225.w),
|
|
|
+ buildPhoneTextFiled(),
|
|
|
+ SizedBox(height: 16.w),
|
|
|
+ buildCodeTextFiled(),
|
|
|
+ SizedBox(height: 46.w),
|
|
|
+ buildLoginButton(),
|
|
|
+ SizedBox(height: 25.w),
|
|
|
+ _buildPrivacy(),
|
|
|
+ SizedBox(height: 100.w),
|
|
|
+ buildOtherLogin(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget buildPhoneTextFiled() {
|
|
|
+ return Container(
|
|
|
+ height: 48.w,
|
|
|
+ margin: EdgeInsets.symmetric(horizontal: 20.w),
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 16.w),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Colors.white,
|
|
|
+ borderRadius: BorderRadius.circular(100.w),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ child: TextField(
|
|
|
+ cursorHeight: 20.w,
|
|
|
+ style: TextStyle(
|
|
|
+ fontSize: 14.sp,
|
|
|
+ color: Colors.black.withAlpha( 204),
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ ),
|
|
|
+ maxLines: 1,
|
|
|
+ maxLength: 11,
|
|
|
+ keyboardType: TextInputType.phone,
|
|
|
+ textAlignVertical: TextAlignVertical.center,
|
|
|
+ textInputAction: TextInputAction.next,
|
|
|
+ decoration: InputDecoration(
|
|
|
+ hintText: StringName.loginEtPhoneHint,
|
|
|
+ counterText: '',
|
|
|
+ hintStyle: TextStyle(
|
|
|
+ fontSize: 14.sp,
|
|
|
+ color: Colors.black.withAlpha(61),
|
|
|
+ fontWeight: FontWeight.normal,
|
|
|
+ ),
|
|
|
+ labelStyle: TextStyle(
|
|
|
+ fontSize: 14.sp,
|
|
|
+ color: ColorName.primaryTextColor,
|
|
|
+ ),
|
|
|
+ contentPadding: const EdgeInsets.all(0),
|
|
|
+ border: const OutlineInputBorder(borderSide: BorderSide.none),
|
|
|
+ enabled: true,
|
|
|
+ ),
|
|
|
+ onChanged: controller.onPhoneChanged,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget buildCodeTextFiled() {
|
|
|
return Container(
|
|
|
- child: Text('Login Page')
|
|
|
+ height: 48.w,
|
|
|
+ margin: EdgeInsets.symmetric(horizontal: 20.w),
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 16.w),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Colors.white,
|
|
|
+ borderRadius: BorderRadius.circular(100.w),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ child: TextField(
|
|
|
+ cursorHeight: 20.w,
|
|
|
+ style: TextStyle(
|
|
|
+ fontSize: 14.sp,
|
|
|
+ color: Colors.black.withAlpha( 204),
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ ),
|
|
|
+ maxLines: 1,
|
|
|
+ maxLength: 4,
|
|
|
+ keyboardType: TextInputType.phone,
|
|
|
+ textAlignVertical: TextAlignVertical.center,
|
|
|
+ textInputAction: TextInputAction.next,
|
|
|
+ decoration: InputDecoration(
|
|
|
+ hintText: StringName.loginPrintVerificationCode,
|
|
|
+ counterText: '',
|
|
|
+ hintStyle: TextStyle(
|
|
|
+ fontSize: 14.sp,
|
|
|
+ color: Colors.black.withAlpha(61),
|
|
|
+ fontWeight: FontWeight.normal,
|
|
|
+ ),
|
|
|
+ labelStyle: TextStyle(
|
|
|
+ fontSize: 14.sp,
|
|
|
+ color: ColorName.primaryTextColor,
|
|
|
+ ),
|
|
|
+ contentPadding: const EdgeInsets.all(0),
|
|
|
+ border: const OutlineInputBorder(borderSide: BorderSide.none),
|
|
|
+ enabled: true,
|
|
|
+ ),
|
|
|
+ onChanged: controller.onCodeChanged,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ buildVerificationCodeSendBtn(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget buildVerificationCodeSendBtn() {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: controller.onSendVerificationCode,
|
|
|
+ child: Container(
|
|
|
+ margin: EdgeInsets.only(left: 12.w, right: 8.w),
|
|
|
+ child: Obx(() {
|
|
|
+ String txt = "";
|
|
|
+
|
|
|
+ if (controller.countDown != null) {
|
|
|
+ txt =
|
|
|
+ '${controller.countDown}${StringName.loginRetransmissionCode}';
|
|
|
+ } else {
|
|
|
+ if (controller.isFirstSend) {
|
|
|
+ txt = StringName.loginSendVerificationCode;
|
|
|
+ } else {
|
|
|
+ txt = StringName.loginResendCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ return Text(
|
|
|
+ txt,
|
|
|
+ style: TextStyle(
|
|
|
+ fontSize: 14.sp,
|
|
|
+ color: controller.isFirstSend
|
|
|
+ ? Colors.black.withAlpha(204)
|
|
|
+ : Color(0xCC7D46FC)
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget buildLoginButton() {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ controller.onLoginClick();
|
|
|
+ },
|
|
|
+ child: Container(
|
|
|
+ height: 48.w,
|
|
|
+ margin: EdgeInsets.symmetric(horizontal: 24.w),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ child: Obx(() {
|
|
|
+ return Container(
|
|
|
+ height: 48.w,
|
|
|
+ decoration:
|
|
|
+ controller.phone.length == 11 &&
|
|
|
+ controller.code.isNotEmpty &&
|
|
|
+ controller.isAgree
|
|
|
+ ? Styles.getActivateButtonDecoration(50.r)
|
|
|
+ : Styles.getInactiveButtonDecoration(50.r),
|
|
|
+ child: Center(
|
|
|
+ child: Text(
|
|
|
+ StringName.login,
|
|
|
+ style: TextStyle(color: Colors.white, fontSize: 16.sp),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildPrivacy() {
|
|
|
+ return Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Obx(() {
|
|
|
+ return GestureDetector(
|
|
|
+ behavior: HitTestBehavior.opaque,
|
|
|
+ onTap: () {
|
|
|
+ controller.clickAgree();
|
|
|
+ },
|
|
|
+ child: Padding(
|
|
|
+ padding: EdgeInsets.symmetric(vertical: 10.w),
|
|
|
+ child:
|
|
|
+ controller.isAgree
|
|
|
+ ? Assets.images.iconLoginAgreePrivacy.image(
|
|
|
+ width: 14.w,
|
|
|
+ height: 14.w,
|
|
|
+ )
|
|
|
+ : Container(
|
|
|
+ padding: EdgeInsets.all(1.w),
|
|
|
+ width: 14.w,
|
|
|
+ height: 14.w,
|
|
|
+ child: Container(
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ shape: BoxShape.circle,
|
|
|
+ border: Border.all(
|
|
|
+ color: Colors.black.withAlpha(153),
|
|
|
+ width: 1.w,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ Text.rich(
|
|
|
+ TextSpan(
|
|
|
+ children: [
|
|
|
+ TextSpan(
|
|
|
+ text: StringName.textSpanIHaveReadAndAgree,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.black.withAlpha(128),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ClickTextSpan(
|
|
|
+ text: StringName.textSpanPrivacyPolicy,
|
|
|
+ url: WebUrl.privacyPolicy,
|
|
|
+ fontSize: 12.sp,
|
|
|
+ color: Colors.black.withAlpha(204),
|
|
|
+ ),
|
|
|
+
|
|
|
+ TextSpan(
|
|
|
+ text: StringName.textSpanAnd,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.black.withAlpha(128),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+
|
|
|
+ ClickTextSpan(
|
|
|
+ text: StringName.textSpanServiceTerms,
|
|
|
+ url: WebUrl.serviceAgreement,
|
|
|
+ color: Colors.black.withAlpha(204),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget buildOtherLogin() {
|
|
|
+ return Container(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ StringName.loginOtherlogin,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.black.withValues(alpha: 0.5),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ height: 0,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ SizedBox(height: 18.h),
|
|
|
+ Material(
|
|
|
+ color: Colors.white,
|
|
|
+ shape: const CircleBorder(),
|
|
|
+ child: InkWell(
|
|
|
+ customBorder: const CircleBorder(), // 保证点击区域是圆的
|
|
|
+ onTap: () {
|
|
|
+ controller.clickWxLogin();
|
|
|
+ },
|
|
|
+ child: SizedBox(
|
|
|
+ width: 44.w,
|
|
|
+ height: 44.w,
|
|
|
+ child: Center(
|
|
|
+ child: Assets.images.iconWechatLogoBlack.image(
|
|
|
+ width: 22.w,
|
|
|
+ height: 22.w,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ SizedBox(height: 6.w),
|
|
|
+ Text(
|
|
|
+ StringName.wechat,
|
|
|
+ textAlign: TextAlign.center,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.black.withValues(alpha: 0.5),
|
|
|
+ fontSize: 12.sp,
|
|
|
+
|
|
|
+ height: 0,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
);
|
|
|
}
|
|
|
}
|