hi-client/lib/app/modules/kr_login/views/kr_login_view.dart

1488 lines
52 KiB
Dart
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/model/enum/kr_request_type.dart';
import 'package:kaer_with_panels/app/model/kr_area_code.dart';
import 'package:kaer_with_panels/app/modules/kr_login/views/kr_search_area_code_view.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import '../../../routes/app_pages.dart';
import '../controllers/kr_login_controller.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/services/api_service/api.dart';
import 'package:kaer_with_panels/app/common/app_config.dart';
import 'package:kaer_with_panels/app/services/kr_site_config_service.dart';
class KRLoginView extends GetView<KRLoginController> {
const KRLoginView({super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
body: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
if (!FocusScope.of(context).hasPrimaryFocus) {
FocusScope.of(context).unfocus();
}
_hideDropdown();
},
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Obx(() {
switch (controller.kr_loginStatus.value) {
case KRLoginProgressStatus.kr_check:
return _buildCheckView(context);
case KRLoginProgressStatus.kr_loginByCode:
return _buildLoginByCodeView(context);
case KRLoginProgressStatus.kr_loginByPsd:
return _buildLoginByPsdView(context);
case KRLoginProgressStatus.kr_registerSendCode:
return _buildRegisterSendCodeView(context);
case KRLoginProgressStatus.kr_registerSetPsd:
return _buildRegisterSetPsdView(context);
case KRLoginProgressStatus.kr_forgetPsdSendCode:
return _buildForgetPsdSendCodeView(context);
case KRLoginProgressStatus.kr_forgetPsdSetPsd:
return _buildForgetPsdSetPsdView(context);
default:
return Container();
}
}),
),
),
);
}
Widget _buildCheckView(BuildContext context) {
// 构建登录视图 - 直接显示邮箱和密码输入框
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// 总是显示返回按钮,包括 kr_check 状态
_buildBackButton(Theme.of(context)),
_buildHeaderSection(Theme.of(context)),
// 邮箱输入框
_buildInputSection(context, Theme.of(context)),
SizedBox(height: 8.w),
// 密码输入框
_buildPasswordInput(Theme.of(context)),
_buildDynamicContent(Theme.of(context)),
_buildNextButton(controller.kr_getNextBtnText(), Theme.of(context)),
SizedBox(height: _getBottomPadding()),
],
);
}
/// 构建密码输入框(专用于 kr_check 状态)
Widget _buildPasswordInput(ThemeData theme) {
return Container(
width: double.infinity,
height: 52.w,
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
Padding(
padding: EdgeInsets.only(left: 12.w),
child: _buildIcon("login_psd"),
),
SizedBox(width: 8.w),
Expanded(
child: Obx(() => TextField(
controller: controller.psdController,
obscureText: controller.kr_obscureText.value,
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: '请输入密码',
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
suffixIcon: controller.kr_psdHasText.value
? IconButton(
icon: Icon(
controller.kr_obscureText.value
? Icons.visibility_off
: Icons.visibility,
color: const Color(0xFF999999),
),
onPressed: () {
controller.kr_obscureText.value = !controller.kr_obscureText.value;
},
)
: null,
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
)),
),
],
),
);
}
/// 获取底部间距
double _getBottomPadding() {
switch (controller.kr_loginStatus.value) {
case KRLoginProgressStatus.kr_check:
case KRLoginProgressStatus.kr_loginByCode:
case KRLoginProgressStatus.kr_loginByPsd:
// 这些状态内容较少,减小底部间距
return 48.w;
case KRLoginProgressStatus.kr_registerSetPsd:
case KRLoginProgressStatus.kr_forgetPsdSetPsd:
// 这些状态有额外的输入框,保持原有间距
return 48.w;
default:
// 其他状态使用中等间距
return 48.w;
}
}
Widget _buildLoginByCodeView(BuildContext context) {
// 构建验证码登录视图的代码
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildBackButton(Theme.of(context)),
_buildHeaderSection(Theme.of(context)),
_buildInputSection(context, Theme.of(context)),
_buildDynamicContent(Theme.of(context)),
_buildNextButton(controller.kr_getNextBtnText(), Theme.of(context)),
SizedBox(height: _getBottomPadding()),
],
);
}
Widget _buildLoginByPsdView(BuildContext context) {
// 构建密码登录视图的代码
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildBackButton(Theme.of(context)),
_buildHeaderSection(Theme.of(context)),
_buildInputSection(context, Theme.of(context)),
_buildDynamicContent(Theme.of(context)),
_buildNextButton(controller.kr_getNextBtnText(), Theme.of(context)),
SizedBox(height: _getBottomPadding()),
],
);
}
Widget _buildRegisterSendCodeView(BuildContext context) {
// 构建单页注册视图 - 所有字段显示在一个页面
final theme = Theme.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildBackButton(theme),
_buildHeaderSection(theme),
// 邮箱输入框
_buildRegistrationEmailInput(theme),
SizedBox(height: 8.w),
// 验证码输入框(根据站点配置决定是否显示,包括底部间距)
_buildRegistrationCodeInputWithSpacing(theme),
// 密码输入框
_buildRegistrationPasswordInput(theme),
SizedBox(height: 8.w),
// 确认密码输入框
_buildRegistrationConfirmPasswordInput(theme),
SizedBox(height: 8.w),
// 邀请码输入框(可选)
_buildInviteCodeInput(theme),
SizedBox(height: 17.w),
// 注册按钮
_buildNextButton('注册账号', theme),
SizedBox(height: _getBottomPadding()),
],
);
}
Widget _buildRegisterSetPsdView(BuildContext context) {
// 构建注册设置密码视图的代码
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildBackButton(Theme.of(context)),
_buildHeaderSection(Theme.of(context)),
_buildInputSection(context, Theme.of(context)),
Column(
children: [
SizedBox(height: 8.w),
_buildInputContainer(context, true, Theme.of(context)),
SizedBox(height: 8.w),
_buildInviteCodeInput(Theme.of(context)),
],
),
_buildDynamicContent(Theme.of(context)),
_buildNextButton(controller.kr_getNextBtnText(), Theme.of(context)),
SizedBox(height: _getBottomPadding()),
],
);
}
Widget _buildForgetPsdSendCodeView(BuildContext context) {
// 构建忘记密码发送验证码视图的代码
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildBackButton(Theme.of(context)),
_buildHeaderSection(Theme.of(context)),
_buildInputSection(context, Theme.of(context)),
_buildDynamicContent(Theme.of(context)),
_buildNextButton(controller.kr_getNextBtnText(), Theme.of(context)),
SizedBox(height: _getBottomPadding()),
],
);
}
Widget _buildForgetPsdSetPsdView(BuildContext context) {
// 构建忘记密码设置密码视图的代码
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildBackButton(Theme.of(context)),
_buildHeaderSection(Theme.of(context)),
_buildInputSection(context, Theme.of(context)),
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSetPsd)
Column(
children: [
SizedBox(height: 8.w),
_buildInputContainer(context, true, Theme.of(context)),
],
),
_buildDynamicContent(Theme.of(context)),
_buildNextButton(controller.kr_getNextBtnText(), Theme.of(context)),
SizedBox(height: _getBottomPadding()),
],
);
}
/// 构建头部文本部分
Widget _buildHeaderSection(ThemeData theme) {
// 根据不同状态决定是否显示额外信息
switch (controller.kr_loginStatus.value) {
case KRLoginProgressStatus.kr_loginByCode:
case KRLoginProgressStatus.kr_registerSendCode:
case KRLoginProgressStatus.kr_forgetPsdSendCode:
// 验证码相关状态
if (!controller.kr_canSendCode.value) {
// 已发送验证码状态
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 24.w),
_buildHeaderText(AppTranslations.kr_login.welcome, theme),
SizedBox(height: 8.w),
_buildHeaderText(
_isSendPhone()
? AppTranslations.kr_login.verifyPhone
: AppTranslations.kr_login.verifyEmail,
theme
),
SizedBox(height: 8.w),
Text(
AppTranslations.kr_login.codeSent(controller.accountController.text),
style: theme.textTheme.bodySmall?.copyWith(
fontSize: 13.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
color: const Color(0xFF666666),
),
),
SizedBox(height: 24.w),
],
);
}
// 未发送验证码状态,显示简单标题
return Padding(
padding: EdgeInsets.only(top: 24.w, bottom: 24.w),
child: _buildHeaderText(AppTranslations.kr_login.welcome, theme),
);
case KRLoginProgressStatus.kr_loginByPsd:
// 密码登录状态
return Padding(
padding: EdgeInsets.only(top: 24.w, bottom: 24.w),
child: _buildHeaderText(AppTranslations.kr_login.welcome, theme),
);
case KRLoginProgressStatus.kr_registerSetPsd:
// 注册设置密码状态
return Padding(
padding: EdgeInsets.only(top: 24.w, bottom: 24.w),
child: _buildHeaderText(AppTranslations.kr_login.welcome, theme),
);
case KRLoginProgressStatus.kr_forgetPsdSetPsd:
// 忘记密码设置新密码状态
return Padding(
padding: EdgeInsets.only(top: 24.w, bottom: 24.w),
child: _buildHeaderText(AppTranslations.kr_login.welcome, theme),
);
case KRLoginProgressStatus.kr_check:
default:
// 初始状态和其他状态
return Padding(
padding: EdgeInsets.only(top: 24.w, bottom: 24.w),
child: _buildHeaderText(AppTranslations.kr_login.welcome, theme),
);
}
}
/// 判断是否为验证码输入状态
bool _isCodeInput() {
switch (controller.kr_loginStatus.value) {
case KRLoginProgressStatus.kr_loginByCode:
case KRLoginProgressStatus.kr_registerSendCode:
case KRLoginProgressStatus.kr_forgetPsdSendCode:
return true;
default:
return false;
}
}
/// 判断是否手机号验证
bool _isSendPhone() {
return controller.kr_loginType == KRLoginType.kr_telephone;
}
/// 修改 _buildInputSection 方法
Widget _buildInputSection(BuildContext context, ThemeData theme) {
return Obx(() {
final loginStatus = controller.kr_loginStatus.value;
if (loginStatus == KRLoginProgressStatus.kr_check) {
return CompositedTransformTarget(
link: controller.kr_layerLink,
child: Container(
width: double.infinity,
child: Row(
children: [
Obx(() => Visibility(
visible: controller.kr_loginType.value == KRLoginType.kr_telephone,
maintainState: true,
maintainSize: false,
maintainAnimation: true,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildAreaSelector(theme),
SizedBox(width: 8.w),
],
),
)),
Expanded(
child: Container(
height: 52.w,
padding: EdgeInsets.symmetric(horizontal: 12.w),
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
Obx(() => Visibility(
visible: controller.kr_loginType.value != KRLoginType.kr_telephone,
maintainState: true,
maintainAnimation: true,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildIcon("login_account"),
SizedBox(width: 8.w),
],
),
)),
Expanded(
child: TextField(
focusNode: controller.kr_accountFocusNode,
controller: controller.accountController,
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: 'login.enterEmailOrPhone'.tr,
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
onChanged: (value) {
if (controller.kr_emailList.isNotEmpty) {
_showDropdown(context);
} else {
_hideDropdown();
}
},
),
),
_buildClearButton(),
],
),
),
),
],
),
),
);
}
return _buildInputContainer(context, false, theme);
});
}
/// 构建输入容器
Widget _buildInputContainer(BuildContext context, bool isNewPsd, ThemeData theme) {
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check &&
controller.kr_loginType.value == KRLoginType.kr_telephone) {
return Row(
children: [
_buildAreaSelector(theme),
SizedBox(width: 8.w),
Expanded(
child: _buildPhoneInput(context, theme),
),
],
);
}
return CompositedTransformTarget(
link: controller.kr_layerLink,
child: Container(
width: double.infinity,
height: 52.w,
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
Padding(
padding: EdgeInsets.only(left: 12.w),
child: _buildInputLeftWideget(theme),
),
SizedBox(width: 8.w),
Expanded(
child: TextField(
controller: controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check
? controller.accountController
: controller.kr_loginStatus.value == KRLoginProgressStatus.kr_loginByCode ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_registerSendCode ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSendCode
? controller.codeController
: isNewPsd
? controller.agPsdController
: controller.psdController,
keyboardType: (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_loginByCode ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_registerSendCode ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSendCode)
? TextInputType.number
: TextInputType.text,
decoration: InputDecoration(
hintText: controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check
? 'login.enterEmailOrPhone'.tr
: controller.kr_loginStatus.value == KRLoginProgressStatus.kr_loginByCode ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_registerSendCode ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSendCode
? 'login.enterCode'.tr
: isNewPsd
? 'login.reenterPassword'.tr
: 'login.enterPassword'.tr,
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
suffixIcon: (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_loginByPsd ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_registerSetPsd ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSetPsd) &&
(isNewPsd ? controller.kr_agPsdHasText.value : controller.kr_psdHasText.value)
? IconButton(
icon: Icon(
controller.kr_obscureText.value ? Icons.visibility_off : Icons.visibility,
color: const Color(0xFF999999),
),
onPressed: () {
controller.kr_obscureText.value = !controller.kr_obscureText.value;
},
)
: null,
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
obscureText: (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_loginByPsd ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_registerSetPsd ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSetPsd) &&
controller.kr_obscureText.value,
onChanged: (value) {
if (controller.kr_emailList.length > 0) {
_showDropdown(context);
} else {
_hideDropdown();
}
},
),
),
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_loginByCode ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_registerSendCode ||
controller.kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSendCode) ...[
if (controller.kr_codeHasText.value)
GestureDetector(
onTap: () {
controller.codeController.clear();
},
child: Container(
height: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: _buildIcon("login_close"),
),
),
_buildSendCodeButton(theme),
] else if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check
? controller.kr_accountHasText.value
: isNewPsd
? controller.kr_agPsdHasText.value
: controller.kr_psdHasText.value) ...[
GestureDetector(
onTap: () {
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check) {
controller.kr_emailList.clear();
_hideDropdown();
}
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check) {
controller.accountController.clear();
} else if (isNewPsd) {
controller.agPsdController.clear();
} else {
controller.psdController.clear();
}
},
child: Container(
height: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: _buildIcon("login_close"),
),
),
],
],
),
),
);
}
/// 构建发送验证码按钮
Widget _buildSendCodeButton(ThemeData theme) {
return Obx(() => GestureDetector(
onTap: controller.kr_canSendCode.value ? () => controller.kr_sendCode() : null,
child: Container(
alignment: Alignment.center,
width: 100.w,
padding: EdgeInsets.only(right: 4.w),
child: Text(
controller.kr_countdownText.value,
style: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
color: controller.kr_canSendCode.value
? const Color(0xFF1797FF)
: const Color(0xFF999999),
fontFamily: 'AlibabaPuHuiTi-Regular',
),
),
),
));
}
/// 显示悬浮框
void _showDropdown(BuildContext context) {
final theme = Theme.of(context);
if (controller.isDropdownVisible) return;
controller.overlayEntry = OverlayEntry(
builder: (_) => Positioned(
width: MediaQuery.of(context).size.width - 40.w,
child: CompositedTransformFollower(
link: controller.kr_layerLink,
showWhenUnlinked: false,
offset: Offset(0, 54.w),
child: Material(
elevation: 4,
borderRadius: BorderRadius.circular(10.r),
child: Obx(() {
return Container(
constraints: BoxConstraints(maxHeight: 216.w),
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.r),
),
),
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: controller.kr_emailList.length,
itemBuilder: (context, index) {
final email = controller.kr_emailList[index];
return InkWell(
onTap: () {
controller.accountController.text = email;
controller.accountController.selection = TextSelection.fromPosition(
TextPosition(offset: email.length),
);
_hideDropdown();
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: 12.w,
horizontal: 15.w,
),
child: Text(
email,
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
),
),
);
},
),
);
}),
),
),
),
);
Overlay.of(context).insert(controller.overlayEntry!);
controller.isDropdownVisible = true;
}
/// 隐藏悬浮框
void _hideDropdown() {
if (controller.isDropdownVisible) {
controller.overlayEntry?.remove();
controller.overlayEntry = null;
controller.isDropdownVisible = false;
}
}
/// 构建输入框左侧组件
Widget _buildInputLeftWideget(ThemeData theme) {
return Obx(() {
switch (controller.kr_loginStatus.value) {
case KRLoginProgressStatus.kr_check:
// 检查状态下,根据登录类型判断返回内容
return controller.kr_loginType.value == KRLoginType.kr_telephone
? _buildChoiceCode(theme)
: _buildIcon("login_account");
case KRLoginProgressStatus.kr_loginByCode:
case KRLoginProgressStatus.kr_registerSendCode:
case KRLoginProgressStatus.kr_forgetPsdSendCode:
// 验证码相关状态,返回通用图标
return _buildIcon("login_code");
case KRLoginProgressStatus.kr_registerSetPsd:
case KRLoginProgressStatus.kr_forgetPsdSetPsd:
// 设置密码相关状态,返回通用图标
return _buildIcon("login_psd");
default:
// 默认返回通用图标
return SizedBox();
}
});
}
/// 动态显示内容部分
Widget _buildDynamicContent(ThemeData theme) {
// 先确定内容
Widget? content;
switch (controller.kr_loginStatus.value) {
case KRLoginProgressStatus.kr_check:
// 在登录状态显示"忘记密码"和切换按钮
content = _buildLoginBtns(theme);
case KRLoginProgressStatus.kr_loginByCode:
case KRLoginProgressStatus.kr_loginByPsd:
content = _buildLoginBtns(theme);
default:
return SizedBox(height: 17.w); // 移除 const因为使用了扩展方法
}
// 有内容时的布局
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 12.w), // 与输入框的间距
content,
SizedBox(height: 17.w), // 与下一步按钮的间距
],
);
}
/// 构建协议文本
Widget _buildAgreementText(ThemeData theme) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
KrLocalImage(
imageName: "selete_s",
width: 20.w,
height: 20.w,
),
SizedBox(width: 4.w),
Expanded(
child: Wrap(
alignment: WrapAlignment.start,
spacing: 4.w,
runSpacing: 4.w,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
'login.agreeTerms'.tr,
style: _commonTextStyle(fontSize: 13),
),
GestureDetector(
onTap: () => Get.toNamed(Routes.KR_WEBVIEW, arguments: {
'url': Api.kr_getSiteTos,
'title': 'login.termsOfService'.tr,
}),
child: Text(
'login.termsOfService'.tr,
style: _commonTextStyle(fontSize: 13, color: const Color(0xFF1796FF)),
),
),
Text(
AppTranslations.kr_login.and,
style: _commonTextStyle(fontSize: 13),
),
GestureDetector(
onTap: () => Get.toNamed(Routes.KR_WEBVIEW, arguments: {
'url': Api.kr_getSitePrivacy,
'title': 'login.privacyPolicy'.tr,
}),
child: Text(
'login.privacyPolicy'.tr,
style: _commonTextStyle(fontSize: 13, color: const Color(0xFF1796FF)),
),
),
],
),
),
],
);
}
/// 构建登录按钮行
Widget _buildLoginBtns(ThemeData theme) {
// 在 kr_check 状态下显示不同的按钮
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check) {
return SizedBox(
height: 24.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
_buildHoverableTextButton(
text: 'login.forgotPassword'.tr,
onTap: () => controller.kr_loginStatus.value =
KRLoginProgressStatus.kr_forgetPsdSendCode,
theme: theme,
),
SizedBox(width: 16.w),
_buildHoverableTextButton(
text: '点我注册',
onTap: () {
// 跳转到注册页面
controller.kr_loginStatus.value = KRLoginProgressStatus.kr_registerSendCode;
},
theme: theme,
),
],
),
);
}
// 其他状态保持原有逻辑
return SizedBox(
height: 24.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () => controller.kr_loginStatus.value =
KRLoginProgressStatus.kr_forgetPsdSendCode,
behavior: HitTestBehavior.opaque,
child: Text(
'login.forgotPassword'.tr,
style: theme.textTheme.bodySmall?.copyWith(
fontSize: 13.sp,
color: const Color(0xFF666666),
fontWeight: FontWeight.w500,
fontFamily: 'AlibabaPuHuiTi-Medium',
),
),
),
GestureDetector(
onTap: () {
controller.kr_loginStatus.value = controller.kr_loginStatus.value ==
KRLoginProgressStatus.kr_loginByPsd
? KRLoginProgressStatus.kr_loginByCode
: KRLoginProgressStatus.kr_loginByPsd;
},
behavior: HitTestBehavior.opaque,
child: Text(
controller.kr_loginStatus.value ==
KRLoginProgressStatus.kr_loginByPsd
? 'login.codeLogin'.tr
: "login.passwordLogin".tr,
style: theme.textTheme.bodySmall?.copyWith(
fontSize: 13.sp,
color: const Color(0xFF666666),
fontWeight: FontWeight.w500,
fontFamily: 'AlibabaPuHuiTi-Medium',
),
),
),
],
),
);
}
/// 构建下一步按钮
Widget _buildNextButton(String text, ThemeData theme) {
return TextButton(
onPressed: () {
switch (controller.kr_loginStatus.value) {
case KRLoginProgressStatus.kr_check:
controller.kr_check();
break;
case KRLoginProgressStatus.kr_loginByCode:
case KRLoginProgressStatus.kr_loginByPsd:
controller.kr_login();
break;
case KRLoginProgressStatus.kr_registerSendCode:
// 直接调用注册,不再需要多步骤
controller.kr_register();
break;
case KRLoginProgressStatus.kr_registerSetPsd:
controller.kr_register();
break;
case KRLoginProgressStatus.kr_forgetPsdSendCode:
controller.kr_checkCode();
break;
case KRLoginProgressStatus.kr_forgetPsdSetPsd:
controller.kr_setNewPsdByForgetPsd();
break;
}
},
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Container(
width: double.infinity,
height: 52.w,
decoration: BoxDecoration(
color: const Color(0xFF1797FF),
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: const Color(0xFF1797FF).withOpacity(0.3),
blurRadius: 8.r,
offset: Offset(0, 4.w),
),
],
),
alignment: Alignment.center,
child: Text(
text,
style: TextStyle(
fontSize: 17.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
letterSpacing: 0.5,
fontFamily: 'AlibabaPuHuiTi-Medium',
),
),
),
);
}
///
Widget _buildIcon(String name) {
return KrLocalImage(
imageName: name,
width: 20.w,
height: 20.w,
);
}
/// 修改区域选择器样式
Widget _buildChoiceCode(ThemeData theme) {
return GestureDetector(
onTap: () {
KRSearchAreaView.show((KRAreaCodeItem selectedArea, int index) {
controller.kr_cutSeleteCodeIndex.value = index;
});
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Obx(() => Text(
controller.kr_areaCodeList[controller.kr_cutSeleteCodeIndex.value].kr_icon,
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 28.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
fontWeight: FontWeight.w400,
height: 1.40,
),
)),
SizedBox(width: 8.w),
Obx(() => Text(
'+${controller.kr_areaCodeList[controller.kr_cutSeleteCodeIndex.value].kr_dialCode}',
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 15.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
fontWeight: FontWeight.w400,
height: 1.40,
),
)),
SizedBox(width: 4.w),
Icon(
Icons.keyboard_arrow_down,
color: theme.textTheme.bodyMedium?.color,
size: 20.w,
),
],
),
),
);
}
/// 通用文本样式
TextStyle _commonTextStyle({
required double fontSize,
FontWeight fontWeight = FontWeight.w400,
Color? color,
}) {
return KrAppTextStyle(
fontSize: fontSize,
fontWeight: fontWeight,
color: color,
);
}
/// 构建标题文本
Widget _buildHeaderText(String text, ThemeData theme) {
return Text(
text,
style: theme.textTheme.headlineMedium?.copyWith(
fontSize: 24.sp,
fontFamily: 'AlibabaPuHuiTi-Bold',
color: theme.textTheme.bodyMedium?.color,
height: 1.3,
),
);
}
Widget _buildInviteCodeInput(ThemeData theme) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 12.w),
height: 52.w,
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
_buildIcon("login_psd"),
SizedBox(width: 8.w),
Expanded(
child: TextField(
controller: controller.inviteCodeController,
decoration: InputDecoration(
hintText: AppTranslations.kr_login.enterInviteCode,
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
),
),
Obx(() => Visibility(
visible: controller.kr_inviteCodeHasText.value,
child: GestureDetector(
onTap: () {
controller.inviteCodeController.clear();
},
child: Container(
height: double.infinity,
padding: EdgeInsets.only(left: 5.w, right: 5.w),
child: _buildIcon("login_close"),
),
),
)),
],
),
);
}
Widget _buildBackButton(ThemeData theme) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
print('👆 返回按钮被点击了!');
print('👆 当前登录状态: ${controller.kr_loginStatus.value}');
// 只有在初始登录状态kr_check返回按钮才返回主页
// 其他所有状态(包括注册页面)都返回到上一步
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check) {
print('👆 调用 Get.back() 返回主页');
Get.back();
print('👆 Get.back() 调用完成');
} else {
// 其他状态:返回到上一步(例如从注册页返回登录页)
print('👆 调用 controller.kr_back() 返回上一步');
controller.kr_back();
}
},
child: Padding(
padding: EdgeInsets.fromLTRB(0, 60.w, 0, 0),
child: Row(
children: [
Icon(
size: 16.w,
Icons.arrow_back_ios_sharp,
color: theme.textTheme.bodyMedium?.color,
),
SizedBox(width: 4.w),
Text(
AppTranslations.kr_login.back,
textAlign: TextAlign.center,
style: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Medium',
fontWeight: FontWeight.w500,
color: theme.textTheme.bodyMedium?.color,
height: 1.60,
),
),
],
),
),
);
}
Widget _buildPhoneInput(BuildContext context, ThemeData theme) {
return Container(
height: 52.w,
padding: EdgeInsets.symmetric(horizontal: 12.w),
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
_buildIcon("login_account"),
SizedBox(width: 8.w),
Expanded(
child: Focus(
onFocusChange: (hasFocus) {
if (hasFocus) {
controller.kr_accountFocusNode.requestFocus();
}
},
child: TextField(
focusNode: controller.kr_accountFocusNode,
controller: controller.accountController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
hintText: 'login.enterEmailOrPhone'.tr,
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
),
),
),
_buildClearButton(),
],
),
);
}
Widget _buildClearButton() {
return Obx(() => Visibility(
visible: controller.kr_accountHasText.value,
child: GestureDetector(
onTap: () {
controller.accountController.clear();
},
child: Container(
height: double.infinity,
padding: EdgeInsets.only(left: 5.w, right: 5.w),
child: _buildIcon("login_close"),
),
),
));
}
Widget _buildAreaSelector(ThemeData theme) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
KRSearchAreaView.show((KRAreaCodeItem selectedArea, int index) {
controller.kr_cutSeleteCodeIndex.value = index;
});
},
child: Container(
height: 52.w,
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: _buildChoiceCode(theme),
),
);
}
/// 构建可悬停的文本按钮
Widget _buildHoverableTextButton({
required String text,
required VoidCallback onTap,
required ThemeData theme,
}) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: StatefulBuilder(
builder: (context, setState) {
bool isHovering = false;
return MouseRegion(
onEnter: (_) => setState(() => isHovering = true),
onExit: (_) => setState(() => isHovering = false),
child: GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Text(
text,
style: theme.textTheme.bodySmall?.copyWith(
fontSize: 13.sp,
color: isHovering ? const Color(0xFF1796FF) : const Color(0xFF666666),
fontWeight: FontWeight.w500,
fontFamily: 'AlibabaPuHuiTi-Medium',
),
),
),
);
},
),
);
}
/// ========== 注册页面相关组件 ==========
/// 构建注册页面的邮箱输入框
Widget _buildRegistrationEmailInput(ThemeData theme) {
return Container(
width: double.infinity,
height: 52.w,
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
Padding(
padding: EdgeInsets.only(left: 12.w),
child: _buildIcon("login_account"),
),
SizedBox(width: 8.w),
Expanded(
child: TextField(
controller: controller.accountController,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'login.enterEmail'.tr,
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
),
),
Obx(() => Visibility(
visible: controller.kr_accountHasText.value,
child: GestureDetector(
onTap: () => controller.accountController.clear(),
child: Container(
height: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: _buildIcon("login_close"),
),
),
)),
],
),
);
}
/// 构建注册页面的验证码输入框(包含间距)
Widget _buildRegistrationCodeInputWithSpacing(ThemeData theme) {
// 从站点配置服务获取验证配置(站点配置不是响应式的,不需要 Obx
final siteConfig = KRSiteConfigService();
final needVerification = siteConfig.isEmailVerificationEnabled() ||
siteConfig.isRegisterVerificationEnabled();
// 如果不需要验证码,返回空容器
if (!needVerification) {
return SizedBox.shrink();
}
// 显示验证码输入框和底部间距
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildRegistrationCodeInput(theme),
SizedBox(height: 8.w),
],
);
}
/// 构建注册页面的验证码输入框(根据站点配置显示)
Widget _buildRegistrationCodeInput(ThemeData theme) {
// 从站点配置服务获取验证配置
final siteConfig = KRSiteConfigService();
final needVerification = siteConfig.isEmailVerificationEnabled() ||
siteConfig.isRegisterVerificationEnabled();
// 如果不需要验证码,返回空容器
if (!needVerification) {
return SizedBox.shrink();
}
// 显示验证码输入框
return Container(
width: double.infinity,
height: 52.w,
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
Padding(
padding: EdgeInsets.only(left: 12.w),
child: _buildIcon("login_code"),
),
SizedBox(width: 8.w),
Expanded(
child: TextField(
controller: controller.codeController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'login.enterCode'.tr,
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
),
),
if (controller.kr_codeHasText.value)
GestureDetector(
onTap: () => controller.codeController.clear(),
child: Container(
height: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: _buildIcon("login_close"),
),
),
_buildSendCodeButton(theme),
],
),
);
}
/// 构建注册页面的密码输入框
Widget _buildRegistrationPasswordInput(ThemeData theme) {
return Container(
width: double.infinity,
height: 52.w,
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
Padding(
padding: EdgeInsets.only(left: 12.w),
child: _buildIcon("login_psd"),
),
SizedBox(width: 8.w),
Expanded(
child: Obx(() => TextField(
controller: controller.psdController,
obscureText: controller.kr_obscureText.value,
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: 'login.enterPassword'.tr,
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
suffixIcon: controller.kr_psdHasText.value
? IconButton(
icon: Icon(
controller.kr_obscureText.value
? Icons.visibility_off
: Icons.visibility,
color: const Color(0xFF999999),
),
onPressed: () {
controller.kr_obscureText.value = !controller.kr_obscureText.value;
},
)
: null,
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
)),
),
],
),
);
}
/// 构建注册页面的确认密码输入框
Widget _buildRegistrationConfirmPasswordInput(ThemeData theme) {
return Container(
width: double.infinity,
height: 52.w,
decoration: ShapeDecoration(
color: theme.cardColor,
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
borderRadius: BorderRadius.circular(10.r),
),
),
child: Row(
children: [
Padding(
padding: EdgeInsets.only(left: 12.w),
child: _buildIcon("login_psd"),
),
SizedBox(width: 8.w),
Expanded(
child: Obx(() => TextField(
controller: controller.agPsdController,
obscureText: controller.kr_obscureText.value,
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: 'login.reenterPassword'.tr,
hintStyle: theme.textTheme.bodySmall?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
suffixIcon: controller.kr_agPsdHasText.value
? IconButton(
icon: Icon(
controller.kr_obscureText.value
? Icons.visibility_off
: Icons.visibility,
color: const Color(0xFF999999),
),
onPressed: () {
controller.kr_obscureText.value = !controller.kr_obscureText.value;
},
)
: null,
),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14.sp,
fontFamily: 'AlibabaPuHuiTi-Regular',
),
)),
),
],
),
);
}
}