import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import '../controllers/login_controller.dart'; import '../../common/webview_page.dart'; import '../../../core/utils/doc_url_helper.dart'; class LoginView extends GetView { const LoginView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; return GestureDetector( onTap: () { FocusScope.of(context).unfocus(); }, child: Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], body: SafeArea( child: _buildWelcomeView(isDarkMode), ), ), ); } // 欢迎页面 Widget _buildWelcomeView(bool isDarkMode) { return Padding( padding: EdgeInsets.all(24.w), child: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Column( children: [ // 应用标志和标题 SizedBox(height: 32.h), _buildAppLogo(isDarkMode), // 欢迎文本 SizedBox(height: 32.h), _buildWelcomeText(isDarkMode), // 输入框和继续按钮 SizedBox(height: 24.h), _buildInputField(isDarkMode), SizedBox(height: 16.h), _buildVerificationInputField(isDarkMode), SizedBox(height: 16.h), _buildContinueButton(isDarkMode), // 社交媒体登录 SizedBox(height: 32.h), _buildSocialLoginSection(isDarkMode), // 用户协议 SizedBox(height: 24.h), _buildTermsAndPrivacy(isDarkMode), SizedBox(height: 24.h), ], ), ), ); } // 应用标志和标题 Widget _buildAppLogo(bool isDarkMode) { return Column( children: [ // 登录页 logo:用 App 自己的图标,替掉原来的「蓝紫渐变 + 耳机 glyph」。 // 与启动页、关于应用、固件页共用 assets/images/app_icon.png: // 那是张不带圆角的正方形(iOS AppIcon 1024 缩到 512), // 圆角靠 ClipRRect 切;渐变底去掉,因为图标自带深色不透明底, // 叠上去只会在圆角处露出一圈脏边。 Container( width: 80.w, height: 80.w, decoration: BoxDecoration( borderRadius: BorderRadius.circular(24.r), boxShadow: [ BoxShadow( color: const Color(0xFF0066CC).withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(24.r), child: Image.asset( 'assets/images/app_icon.png', width: 80.w, height: 80.w, fit: BoxFit.cover, ), ), ), SizedBox(height: 16.h), Text( 'Eaimar', style: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), SizedBox(height: 8.h), Text( 'slogan'.tr, // 连接世界,超越声音 style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[400] : Colors.black54, ), ), ], ); } // 欢迎文本 Widget _buildWelcomeText(bool isDarkMode) { return Column( children: [ Text( 'welcome'.tr, // 欢迎使用 style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), ), SizedBox(height: 4.h), if (!controller.isChinaMainland.value) Text( 'loginTips'.tr, // 请使用手机号或邮箱登录/注册 style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[400] : Colors.black54, ), ), ], ); } // 输入框 Widget _buildInputField(bool isDarkMode) { return Container( decoration: BoxDecoration( color: isDarkMode ? Colors.grey[850]!.withOpacity(0.6) // 更深的背景色,增加不透明度 : Colors.grey.withOpacity(0.1), borderRadius: BorderRadius.circular(16.r), border: Border.all( color: isDarkMode ? Colors.grey[700]! : Colors.transparent, // 暗黑模式添加边框 width: 1, ), boxShadow: isDarkMode ? [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 4, offset: const Offset(0, 2), ) ] : [], // 暗黑模式添加阴影 ), child: Padding( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 4.h), child: Row( children: [ // 添加国家选择按钮 InkWell( onTap: () => _showCountryPicker(isDarkMode), child: Container( padding: EdgeInsets.symmetric(horizontal: 4.w), child: Row( mainAxisSize: MainAxisSize.min, children: [ Obx(() => Text( '${controller.selectedCountryFlag}${controller.selectedCountryCode}', style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.white : Colors.black, ), )), if (!controller.isChinaMainland.value) Icon( Icons.arrow_drop_down, color: isDarkMode ? Colors.grey[400] : Colors.black54, size: 20.sp, ), ], ), ), ), // 分隔线 Container( height: 20.h, width: 1, color: isDarkMode ? Colors.grey[600] : Colors.grey[300], // 调整分隔线颜色 margin: EdgeInsets.symmetric(horizontal: 8.w), ), Expanded( child: TextField( controller: controller.contactController, decoration: InputDecoration( hintText: controller.selectedCountryFlag == '✉️' ? 'email'.tr // 邮箱 : 'phoneNumber'.tr, // 手机号码 hintStyle: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[500] : Colors.black38, // 调整提示文字颜色 ), border: InputBorder.none, fillColor: Colors.transparent, filled: true, focusedBorder: InputBorder.none, ), style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.white : Colors.black, ), ), ), ], ), ), ); } // 添加国家选择底部弹窗 void _showCountryPicker(bool isDarkMode) { // 中国大陆用户不显示国家选择器 if (controller.isChinaMainland.value) { return; } Get.bottomSheet( Container( height: 400.h, padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: isDarkMode ? Colors.grey[900] : Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(20.r), topRight: Radius.circular(20.r), ), ), child: Column( children: [ Text( 'selectCountry'.tr, // 选择国家/地区 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), SizedBox(height: 16.h), Expanded( child: ListView.builder( itemCount: controller.countryCodes.length, itemBuilder: (context, index) { final country = controller.countryCodes[index]; return ListTile( onTap: () { controller.updateCountryCode( country['code']!, country['flag']!, ); Get.back(); }, leading: Text( country['flag']!, style: TextStyle(fontSize: 24.sp), ), title: Text( '${country['name']}'.tr, // 国家名称 style: TextStyle( color: isDarkMode ? Colors.white : Colors.black, ), ), trailing: Text( country['code']!, style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[600], ), ), ); }, ), ), ], ), ), backgroundColor: Colors.transparent, isScrollControlled: true, ); } // 验证码输入框 Widget _buildVerificationInputField(bool isDarkMode) { return Container( decoration: BoxDecoration( color: isDarkMode ? Colors.grey[850]!.withOpacity(0.6) // 更深的背景色,增加不透明度 : Colors.grey.withOpacity(0.1), borderRadius: BorderRadius.circular(16.r), border: Border.all( color: isDarkMode ? Colors.grey[700]! : Colors.transparent, // 暗黑模式添加边框 width: 1, ), boxShadow: isDarkMode ? [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 4, offset: const Offset(0, 2), ) ] : [], // 暗黑模式添加阴影 ), child: Padding( padding: EdgeInsets.only(left: 16.w, right: 4.w, top: 4.h, bottom: 4.h), child: Row( children: [ Icon( Icons.lock_outline, color: isDarkMode ? Colors.grey[400] : Colors.black54, size: 20.sp, ), SizedBox(width: 12.w), Expanded( child: GetBuilder( builder: (controller) => TextField( controller: controller.verificationCodeController, keyboardType: TextInputType.number, maxLength: 6, decoration: InputDecoration( hintText: 'verificationCode'.tr, // 验证码 hintStyle: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[500] : Colors.black38, // 调整提示文字颜色 ), border: InputBorder.none, counterText: '', fillColor: Colors.transparent, filled: true, focusedBorder: InputBorder.none, ), style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.white : Colors.black, ), ), ), ), Obx(() => TextButton( onPressed: controller.countdownSeconds > 0 ? null : controller.getVerificationCode, style: TextButton.styleFrom( padding: EdgeInsets.symmetric(horizontal: 16.w), foregroundColor: const Color(0xFF3B82F6), backgroundColor: Colors.transparent, disabledForegroundColor: isDarkMode ? Colors.grey[600] : Colors.grey[400], ), child: Text( controller.countdownSeconds > 0 ? '${controller.countdownSeconds}${'reacquireAfter'.tr}' // s后重新获取 : 'getCode'.tr, // 获取验证码 style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w500, ), ), )), ], ), ), ); } // 继续按钮 Widget _buildContinueButton(bool isDarkMode) { return SizedBox( width: double.infinity, child: Obx(() => ElevatedButton( onPressed: controller.isLoading ? null : () => controller.login(), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0066CC), foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 14.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), elevation: 0, disabledBackgroundColor: const Color(0xFF0066CC).withOpacity(0.7), disabledForegroundColor: Colors.white, ), child: controller.isLoading ? SizedBox( height: 20.h, width: 20.h, child: CircularProgressIndicator( strokeWidth: 2.w, valueColor: const AlwaysStoppedAnimation(Colors.white), ), ) : Text( 'continue'.tr, // 继续 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), )), ); } // 社交媒体登录部分 Widget _buildSocialLoginSection(bool isDarkMode) { return Column( children: [ Row( children: [ Expanded( child: Divider( color: isDarkMode ? Colors.grey[800] : Colors.grey[300])), Padding( padding: EdgeInsets.symmetric(horizontal: 16.w), child: Text( 'otherWays'.tr, // 或使用以下方式登录 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.grey[500] : Colors.grey[600], ), ), ), Expanded( child: Divider( color: isDarkMode ? Colors.grey[800] : Colors.grey[300])), ], ), SizedBox(height: 24.h), // 必须是 Obx:游客登录入口的显隐要等服务端配置回来(异步),用 Builder 只会渲染 // onInit 时的初值 false,入口再也不会出现。isChinaMainland 同为 .obs,一并受益。 Obx( () { final buttons = []; if (controller.isChinaMainland.value) { buttons.add(_buildSocialButton( FontAwesomeIcons.weixin, Colors.green, isDarkMode)); } else if (GetPlatform.isAndroid) { buttons.add(_buildSocialButton( FontAwesomeIcons.google, Colors.red, isDarkMode)); } if (GetPlatform.isIOS) { buttons.add(_buildSocialButton( FontAwesomeIcons.apple, Colors.black, isDarkMode)); } if (controller.isGuestLoginVisible) { buttons.add(_buildSocialButton( FontAwesomeIcons.userSecret, Colors.black, isDarkMode)); } return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ for (int i = 0; i < buttons.length; i++) ...[ if (i > 0) SizedBox(width: 24.w), buttons[i], ], ], ); }, ), ], ); } // 社交媒体按钮 Widget _buildSocialButton(IconData icon, Color color, bool isDarkMode) { return Container( width: 48.w, height: 48.w, decoration: BoxDecoration( color: isDarkMode ? Colors.grey[800] : Colors.white, borderRadius: BorderRadius.circular(24.r), boxShadow: isDarkMode ? [] : [ BoxShadow( color: Colors.grey.withOpacity(0.2), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: IconButton( icon: FaIcon( icon, color: color, size: 20.sp, ), onPressed: () => SocialLogin(icon)), ); } Future SocialLogin(IconData icon) async { switch (icon) { case FontAwesomeIcons.facebook: { controller.loginType = 4; break; } case FontAwesomeIcons.google: { controller.loginType = 3; break; } case FontAwesomeIcons.weixin: { controller.loginType = 2; break; } case FontAwesomeIcons.apple: // 处理Apple登录 { controller.loginType = 5; break; } case FontAwesomeIcons.userSecret: // 处理游客登录 { controller.loginType = 6; break; } } await controller.login(fromSocial: true); } // 用户协议和隐私政策 Widget _buildTermsAndPrivacy(bool isDarkMode) { return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ // 将勾选框和文本包装在 GestureDetector 中 GestureDetector( onTap: () => controller.hasAcceptedPrivacy = !controller.hasAcceptedPrivacy, behavior: HitTestBehavior.opaque, // 确保透明区域也能响应点击 child: Row( children: [ SizedBox( width: 12.w, height: 16.w, child: Transform.scale( scale: 0.7, child: Obx(() => Checkbox( value: controller.hasAcceptedPrivacy, onChanged: (value) => controller.hasAcceptedPrivacy = value ?? false, activeColor: const Color(0xFF3B82F6), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: VisualDensity.compact, side: BorderSide( width: 1.6, color: Colors.grey[600]!, ), )), ), ), SizedBox(width: 4.w), Text( 'expressConsent'.tr, // 登录/注册即表示你同意 style: TextStyle( fontSize: 13.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[700], ), ), ], ), ), ], ), //SizedBox(height: 4.h), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ GestureDetector( onTap: () { Get.to(() => WebViewPage( url: userAgreementUrl(), title: 'userAgreement'.tr, )); }, child: Text( 'userAgreement'.tr, // 《用户协议》 style: TextStyle( fontSize: 12.sp, color: const Color(0xFF3B82F6), ), ), ), Text( 'and'.tr, // 和 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.grey[500] : Colors.grey[600], ), ), GestureDetector( onTap: () { Get.to(() => WebViewPage( url: privacyPolicyUrl(), title: 'privacyPolicy'.tr, )); }, child: Text( 'privacyPolicy'.tr, // 《隐私政策》 style: TextStyle( fontSize: 12.sp, color: const Color(0xFF3B82F6), ), ), ), ], ), ], ); } }