import 'dart:async'; import 'dart:io'; import 'package:ainoval/blocs/auth/auth_bloc.dart'; import 'package:ainoval/models/app_registration_config.dart'; import 'package:ainoval/screens/novel_list/novel_list_real_data_screen.dart'; import 'package:ainoval/widgets/common/theme_toggle_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:ainoval/widgets/common/top_toast.dart'; /// 登录页面 class LoginScreen extends StatefulWidget { const LoginScreen({Key? key}) : super(key: key); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _formKey = GlobalKey(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); final _emailController = TextEditingController(); final _phoneController = TextEditingController(); final _verificationCodeController = TextEditingController(); final _captchaController = TextEditingController(); bool _isLogin = true; // 是否为登录模式 String _loginMethod = 'username'; // 登录方式: username, phone, email RegistrationMethod? _registrationMethod; // 注册方式: email, phone String? _captchaId; String? _captchaImage; bool _isVerificationCodeSent = false; int _countdown = 0; bool _hasNetworkConnection = true; StreamSubscription>? _connectivitySubscription; RegistrationConfig? _registrationConfig; Timer? _countdownTimer; @override void initState() { super.initState(); _loadRegistrationConfig(); if (!_isLogin) { _loadCaptcha(); } _initNetworkListener(); } @override void dispose() { _usernameController.dispose(); _passwordController.dispose(); _emailController.dispose(); _phoneController.dispose(); _verificationCodeController.dispose(); _captchaController.dispose(); _connectivitySubscription?.cancel(); _countdownTimer?.cancel(); super.dispose(); } /// 初始化网络连接监听 void _initNetworkListener() { _connectivitySubscription = Connectivity().onConnectivityChanged.listen( (List results) { final isConnected = results.any((result) => result != ConnectivityResult.none); if (mounted) { setState(() { _hasNetworkConnection = isConnected; }); if (!isConnected) { _showNetworkError(); } } }, ); } /// 检查网络连接 Future _checkNetworkConnection() async { try { final result = await InternetAddress.lookup('google.com'); return result.isNotEmpty && result[0].rawAddress.isNotEmpty; } catch (e) { return false; } } /// 显示网络错误提示 void _showNetworkError() { TopToast.warning(context, '网络连接已断开,请检查您的网络连接'); () async { final isConnected = await _checkNetworkConnection(); if (mounted) { setState(() { _hasNetworkConnection = isConnected; }); if (isConnected) { TopToast.success(context, '网络连接已恢复'); } } }(); } /// 加载注册配置 Future _loadRegistrationConfig() async { final config = RegistrationConfig( phoneRegistrationEnabled: await AppRegistrationConfig.isPhoneRegistrationEnabled(), emailRegistrationEnabled: await AppRegistrationConfig.isEmailRegistrationEnabled(), verificationRequired: await AppRegistrationConfig.isVerificationRequired(), quickRegistrationEnabled: await AppRegistrationConfig.isQuickRegistrationEnabled(), ); setState(() { _registrationConfig = config; // 设置默认注册方式为第一个可用的方式 if (config.availableMethods.isNotEmpty) { _registrationMethod = config.availableMethods.first; } }); } /// 切换登录/注册模式 void _toggleMode() { setState(() { _isLogin = !_isLogin; _loginMethod = 'username'; // 重置登录方式 if (!_isLogin) { if (!(_registrationConfig?.quickRegistrationEnabled ?? true)) { _loadCaptcha(); // 需要验证码的注册才加载 } // 设置默认注册方式 if (_registrationConfig != null && _registrationConfig!.availableMethods.isNotEmpty) { _registrationMethod = _registrationConfig!.availableMethods.first; } } }); _formKey.currentState?.reset(); // 重置表单验证状态 } /// 加载图片验证码 Future _loadCaptcha() async { final authBloc = context.read(); authBloc.add(LoadCaptcha()); } /// 发送验证码 Future _sendVerificationCode() async { final authBloc = context.read(); String type = ''; String target = ''; if (_isLogin) { // 登录时的验证码发送 if (_loginMethod == 'phone') { type = 'phone'; target = _phoneController.text; if (!RegExp(r'^1[3-9]\d{9}').hasMatch(target)) { _showError('请输入正确的手机号'); return; } } else if (_loginMethod == 'email') { type = 'email'; target = _emailController.text; if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}').hasMatch(target)) { _showError('请输入正确的邮箱地址'); return; } } } else { // 注册时的验证码发送(快捷注册不开启验证码) if (_registrationConfig?.quickRegistrationEnabled ?? true) { return; } if (_registrationMethod == RegistrationMethod.email) { type = 'email'; target = _emailController.text; if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}').hasMatch(target)) { _showError('请输入正确的邮箱地址'); return; } } else if (_registrationMethod == RegistrationMethod.phone) { type = 'phone'; target = _phoneController.text; if (!RegExp(r'^1[3-9]\d{9}').hasMatch(target)) { _showError('请输入正确的手机号'); return; } } } if (type.isNotEmpty) { authBloc.add(SendVerificationCode( type: type, target: target, purpose: _isLogin ? 'login' : 'register', )); // 开始倒计时 _startCountdown(); } } /// 开始倒计时 void _startCountdown() { setState(() { _isVerificationCodeSent = true; _countdown = 60; }); _countdownTimer?.cancel(); _countdownTimer = Timer.periodic(Duration(seconds: 1), (timer) { if (_countdown > 0) { setState(() { _countdown--; }); } else { timer.cancel(); setState(() { _isVerificationCodeSent = false; }); } }); } /// 显示错误消息 void _showError(String message) { TopToast.error(context, message); } /// 提交表单 - 改为向 AuthBloc 发送事件 void _submitForm() async { if (_formKey.currentState!.validate()) { // 检查网络连接 if (!_hasNetworkConnection) { final isConnected = await _checkNetworkConnection(); if (!isConnected) { _showError('请检查您的网络连接后再试'); return; } else { setState(() { _hasNetworkConnection = true; }); } } // 获取 AuthBloc 实例 final authBloc = context.read(); if (_isLogin) { // 根据登录方式发送不同的登录事件 switch (_loginMethod) { case 'phone': authBloc.add(PhoneLogin( phone: _phoneController.text, verificationCode: _verificationCodeController.text, )); break; case 'email': authBloc.add(EmailLogin( email: _emailController.text, verificationCode: _verificationCodeController.text, )); break; default: authBloc.add(AuthLogin( username: _usernameController.text, password: _passwordController.text, )); } } else { // 注册 final quick = _registrationConfig?.quickRegistrationEnabled ?? true; if (quick) { // 仅用户名 + 密码 authBloc.add(AuthRegister( username: _usernameController.text, password: _passwordController.text, email: null, phone: null, displayName: _usernameController.text, captchaId: null, captchaCode: null, emailVerificationCode: null, phoneVerificationCode: null, )); } else { // 旧流程 String? email; String? phone; String? emailVerificationCode; String? phoneVerificationCode; if (_registrationMethod == RegistrationMethod.email) { email = _emailController.text; emailVerificationCode = _verificationCodeController.text; } else if (_registrationMethod == RegistrationMethod.phone) { phone = _phoneController.text; phoneVerificationCode = _verificationCodeController.text; } // 发送注册事件 authBloc.add(AuthRegister( username: _usernameController.text, password: _passwordController.text, email: email, phone: phone, displayName: _usernameController.text, captchaId: _captchaId, captchaCode: _captchaController.text, emailVerificationCode: emailVerificationCode, phoneVerificationCode: phoneVerificationCode, )); } } } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDarkMode = theme.brightness == Brightness.dark; final quick = _registrationConfig?.quickRegistrationEnabled ?? true; return Scaffold( backgroundColor: isDarkMode ? Colors.grey[900] : Colors.grey[100], body: BlocConsumer( listenWhen: (prev, curr) => curr is AuthAuthenticated || curr is AuthUnauthenticated, listener: (context, state) { // --- 处理认证成功后的导航 --- if (state is AuthAuthenticated) { // 确保在 widget 仍然挂载时执行导航 if (mounted) { // 导航到小说列表页面 // 使用 pushReplacement 避免用户返回登录页 Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const NovelListRealDataScreen()), ); } } }, buildWhen: (prev, curr) => curr is AuthAuthenticated || curr is AuthUnauthenticated, builder: (context, state) { // 根据 BLoC 状态判断是否显示加载状态 final bool isLoading = state is AuthLoading; // 从 BLoC 状态获取错误信息 final String? errorMessage = state is AuthError ? state.message : null; // 处理验证码状态 if (state is CaptchaLoaded) { _captchaId = state.captchaId; _captchaImage = state.captchaImage; } return Stack( children: [ // 主题切换按钮放在右上角 Positioned( top: 50, right: 20, child: const ThemeToggleButton(), ), // 原有的登录表单内容 Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: Container( constraints: const BoxConstraints(maxWidth: 400), child: Card( elevation: 8.0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.0), ), child: Padding( padding: const EdgeInsets.all(24.0), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Icon( Icons.biotech, size: 60, color: theme.colorScheme.primary, ), const SizedBox(height: 16), Text( 'AINoval', textAlign: TextAlign.center, style: theme.textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.primary, ), ), const SizedBox(height: 8), Text( _isLogin ? '登录您的创作平台' : '加入AINoval开始创作', textAlign: TextAlign.center, style: theme.textTheme.titleMedium?.copyWith( color: theme.textTheme.bodySmall?.color, ), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: theme.colorScheme.primary.withOpacity(0.08), borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.card_giftcard, size: 18, color: theme.colorScheme.primary, ), const SizedBox(width: 6), Text( '测试阶段福利:注册即送300积分', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.primary, fontWeight: FontWeight.w600, ), ), ], ), ), const SizedBox(height: 32), if (errorMessage != null) Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: theme.colorScheme.errorContainer, borderRadius: BorderRadius.circular(8), border: Border.all( color: theme.colorScheme.error.withOpacity(0.3), width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( _getErrorIcon(errorMessage), color: theme.colorScheme.onErrorContainer, size: 20, ), const SizedBox(width: 8), Expanded( child: Text( errorMessage, style: TextStyle( color: theme.colorScheme.onErrorContainer, fontWeight: FontWeight.w500, ), ), ), ], ), if (_shouldShowRetryButton(errorMessage)) ...[ const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: isLoading ? null : () { _retryLastAction(); }, style: TextButton.styleFrom( foregroundColor: theme.colorScheme.onErrorContainer, padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 4, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.refresh, size: 16), const SizedBox(width: 4), Text('重试'), ], ), ), ], ), ], ], ), ), // 登录方式选择(仅登录时显示) if (_isLogin) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ChoiceChip( label: Text('用户名'), selected: _loginMethod == 'username', onSelected: (selected) { if (selected) { setState(() { _loginMethod = 'username'; }); } }, ), ChoiceChip( label: Text('手机号'), selected: _loginMethod == 'phone', onSelected: (selected) { if (selected) { setState(() { _loginMethod = 'phone'; }); } }, ), ChoiceChip( label: Text('邮箱'), selected: _loginMethod == 'email', onSelected: (selected) { if (selected) { setState(() { _loginMethod = 'email'; }); } }, ), ], ), const SizedBox(height: 16), ], // 根据登录方式或注册模式显示不同的输入字段 if (_isLogin && _loginMethod == 'username') ...[ TextFormField( controller: _usernameController, decoration: InputDecoration( labelText: '用户名', prefixIcon: Icon(Icons.person_outline, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), validator: (value) { if (value == null || value.isEmpty) { return '请输入用户名'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, decoration: InputDecoration( labelText: '密码', prefixIcon: Icon(Icons.lock_outline, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), obscureText: true, validator: (value) { if (value == null || value.isEmpty) { return '请输入密码'; } return null; }, ), ] else if (_isLogin && _loginMethod == 'phone') ...[ // 保持原有手机号验证码登录 TextFormField( controller: _phoneController, decoration: InputDecoration( labelText: '手机号', prefixIcon: Icon(Icons.phone_outlined, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), keyboardType: TextInputType.phone, validator: (value) { if (value == null || value.isEmpty) { return '请输入手机号'; } if (!RegExp(r'^1[3-9]\d{9}$').hasMatch(value)) { return '请输入正确的手机号'; } return null; }, ), const SizedBox(height: 16), Row( children: [ Expanded( flex: 2, child: TextFormField( controller: _verificationCodeController, decoration: InputDecoration( labelText: '验证码', prefixIcon: Icon(Icons.lock_outline, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) { return '请输入验证码'; } if (!RegExp(r'^\d{6}$').hasMatch(value)) { return '验证码为6位数字'; } return null; }, ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: _isVerificationCodeSent ? null : _sendVerificationCode, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0), ), ), child: Text( _isVerificationCodeSent ? '$_countdown秒' : '获取验证码', style: const TextStyle(fontSize: 14), ), ), ), ], ), ] else if (_isLogin && _loginMethod == 'email') ...[ // 保持原有邮箱验证码登录 TextFormField( controller: _emailController, decoration: InputDecoration( labelText: '邮箱', prefixIcon: Icon(Icons.email_outlined, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), keyboardType: TextInputType.emailAddress, validator: (value) { if (value == null || value.isEmpty) { return '请输入邮箱'; } if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') .hasMatch(value)) { return '请输入有效的邮箱地址'; } return null; }, ), const SizedBox(height: 16), Row( children: [ Expanded( flex: 2, child: TextFormField( controller: _verificationCodeController, decoration: InputDecoration( labelText: '验证码', prefixIcon: Icon(Icons.lock_outline, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) { return '请输入验证码'; } if (!RegExp(r'^\d{6}$').hasMatch(value)) { return '验证码为6位数字'; } return null; }, ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: _isVerificationCodeSent ? null : _sendVerificationCode, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0), ), ), child: Text( _isVerificationCodeSent ? '$_countdown秒' : '获取验证码', style: const TextStyle(fontSize: 14), ), ), ), ], ), ] else if (!_isLogin) ...[ // 注册表单(根据快捷注册开关调整显示) TextFormField( controller: _usernameController, decoration: InputDecoration( labelText: '用户名', prefixIcon: Icon(Icons.person_outline, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), validator: (value) { if (value == null || value.isEmpty) { return '请输入用户名'; } if (value.length < 3 || value.length > 20) { return '用户名长度必须在3-20个字符之间'; } if (!RegExp(r'^[a-zA-Z0-9_]+$').hasMatch(value)) { return '用户名只能包含字母、数字和下划线'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, decoration: InputDecoration( labelText: '密码', prefixIcon: Icon(Icons.lock_outline, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), obscureText: true, validator: (value) { if (value == null || value.isEmpty) { return '请输入密码'; } if (value.length < 6) { return '密码长度至少为6位'; } return null; }, ), if (!quick) ...[ const SizedBox(height: 16), // 邮箱输入(选填) TextFormField( controller: _emailController, decoration: InputDecoration( labelText: '邮箱(选填)', prefixIcon: Icon(Icons.email_outlined, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), suffixIcon: _emailController.text.isNotEmpty ? TextButton( onPressed: _isVerificationCodeSent ? null : _sendVerificationCode, child: Text( _isVerificationCodeSent ? '$_countdown秒' : '发送验证码', style: TextStyle(fontSize: 12), ), ) : null, ), keyboardType: TextInputType.emailAddress, onChanged: (value) { setState(() {}); // 刷新以显示/隐藏发送验证码按钮 }, validator: (value) { if (value != null && value.isNotEmpty) { if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') .hasMatch(value)) { return '请输入有效的邮箱地址'; } } return null; }, ), const SizedBox(height: 16), // 手机号输入(选填) TextFormField( controller: _phoneController, decoration: InputDecoration( labelText: '手机号(选填)', prefixIcon: Icon(Icons.phone_outlined, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), suffixIcon: _phoneController.text.isNotEmpty ? TextButton( onPressed: _isVerificationCodeSent ? null : _sendVerificationCode, child: Text( _isVerificationCodeSent ? '$_countdown秒' : '发送验证码', style: TextStyle(fontSize: 12), ), ) : null, ), keyboardType: TextInputType.phone, onChanged: (value) { setState(() {}); // 刷新以显示/隐藏发送验证码按钮 }, validator: (value) { if (value != null && value.isNotEmpty) { if (!RegExp(r'^1[3-9]\d{9}$').hasMatch(value)) { return '请输入正确的手机号'; } } return null; }, ), // 如果填写了邮箱或手机号,显示验证码输入框 if (_emailController.text.isNotEmpty || _phoneController.text.isNotEmpty) ...[ const SizedBox(height: 16), TextFormField( controller: _verificationCodeController, decoration: InputDecoration( labelText: '验证码', prefixIcon: Icon(Icons.lock_outline, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), keyboardType: TextInputType.number, validator: (value) { if (_emailController.text.isNotEmpty || _phoneController.text.isNotEmpty) { if (value == null || value.isEmpty) { return '请输入验证码'; } if (!RegExp(r'^\d{6}$').hasMatch(value)) { return '验证码为6位数字'; } } return null; }, ), ], const SizedBox(height: 16), // 图片验证码 Row( children: [ Expanded( flex: 2, child: TextFormField( controller: _captchaController, decoration: InputDecoration( labelText: '图片验证码', prefixIcon: Icon(Icons.security, color: theme.iconTheme.color?.withOpacity(0.7)), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.0), ), filled: true, fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[200], contentPadding: const EdgeInsets.symmetric( vertical: 16.0, horizontal: 12.0), ), validator: (value) { if (value == null || value.isEmpty) { return '请输入验证码'; } if (value.length != 4) { return '验证码长度为4位'; } return null; }, ), ), const SizedBox(width: 12), Container( width: 100, height: 50, decoration: BoxDecoration( border: Border.all( color: theme.dividerColor, ), borderRadius: BorderRadius.circular(8), ), child: InkWell( onTap: _loadCaptcha, child: _captchaImage != null ? ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.memory( Uri.parse(_captchaImage!).data!.contentAsBytes(), fit: BoxFit.cover, ), ) : Center( child: CircularProgressIndicator( strokeWidth: 2, ), ), ), ), ], ), ], ], const SizedBox(height: 24), ElevatedButton( onPressed: isLoading ? null : _submitForm, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0), ), backgroundColor: _hasNetworkConnection ? theme.colorScheme.primary : theme.colorScheme.outline, foregroundColor: _hasNetworkConnection ? theme.colorScheme.onPrimary : theme.colorScheme.onSurface.withOpacity(0.5), ), child: isLoading ? SizedBox( height: 24, width: 24, child: CircularProgressIndicator( strokeWidth: 3, color: theme.colorScheme.onPrimary, ), ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (!_hasNetworkConnection) ...[ Icon(Icons.wifi_off, size: 20), SizedBox(width: 8), ], Text( !_hasNetworkConnection ? '网络断开' : (_isLogin ? '登 录' : '注 册'), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), ), const SizedBox(height: 16), TextButton( onPressed: isLoading ? null : _toggleMode, style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), ), child: Text( _isLogin ? '还没有账户?立即注册' : '已有账户?前往登录', style: TextStyle( color: theme.colorScheme.primary, fontWeight: FontWeight.w600, ), ), ), ], ), ), ), ), ), ), ), ], ); }, ), ); } /// 根据错误消息获取对应的图标 IconData _getErrorIcon(String errorMessage) { final lowerMessage = errorMessage.toLowerCase(); if (lowerMessage.contains('网络') || lowerMessage.contains('连接')) { return Icons.wifi_off; } else if (lowerMessage.contains('密码') || lowerMessage.contains('用户名')) { return Icons.key_off; } else if (lowerMessage.contains('验证码')) { return Icons.security; } else if (lowerMessage.contains('服务器')) { return Icons.dns; } else if (lowerMessage.contains('超时')) { return Icons.timer_off; } else { return Icons.error_outline; } } /// 判断是否应该显示重试按钮 bool _shouldShowRetryButton(String errorMessage) { final lowerMessage = errorMessage.toLowerCase(); // 对于以下类型的错误显示重试按钮 return lowerMessage.contains('网络') || lowerMessage.contains('连接') || lowerMessage.contains('超时') || lowerMessage.contains('服务器') || lowerMessage.contains('请稍后重试'); } /// 重试最后的操作 void _retryLastAction() { // 根据当前状态重试相应操作 if (_isLogin) { _submitForm(); } else { // 注册模式下:如果是非快捷注册,可能需要重新加载验证码 if (!(_registrationConfig?.quickRegistrationEnabled ?? true)) { _loadCaptcha(); } } } }