import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_text_styles.dart'; import '../../../core/theme/app_dimensions.dart'; import '../../../core/widgets/custom_button.dart'; import '../../../core/widgets/custom_text_field.dart'; import '../../../core/routes/app_routes.dart'; import '../providers/auth_provider.dart'; /// 注册页面 class RegisterScreen extends ConsumerStatefulWidget { const RegisterScreen({super.key}); @override ConsumerState createState() => _RegisterScreenState(); } class _RegisterScreenState extends ConsumerState { final _formKey = GlobalKey(); final _usernameController = TextEditingController(); final _nicknameController = TextEditingController(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _confirmPasswordController = TextEditingController(); bool _obscurePassword = true; bool _obscureConfirmPassword = true; bool _agreeToTerms = false; bool _isLoading = false; // 密码强度提示 bool _hasMinLength = false; bool _hasNumber = false; bool _hasLowerCase = false; bool _hasUpperCase = false; bool _hasSpecialChar = false; @override void initState() { super.initState(); // 监听密码输入,实时验证 _passwordController.addListener(_validatePasswordStrength); } /// 验证密码强度 void _validatePasswordStrength() { final password = _passwordController.text; setState(() { _hasMinLength = password.length >= 8; _hasNumber = RegExp(r'[0-9]').hasMatch(password); _hasLowerCase = RegExp(r'[a-z]').hasMatch(password); _hasUpperCase = RegExp(r'[A-Z]').hasMatch(password); _hasSpecialChar = RegExp(r'[!@#\$%^&*()_+\-=\[\]{};:"\\|,.<>\/?]').hasMatch(password); }); } @override void dispose() { _usernameController.dispose(); _nicknameController.dispose(); _emailController.dispose(); _passwordController.dispose(); _confirmPasswordController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back_ios, color: AppColors.onSurface), onPressed: () => Navigator.of(context).pop(), ), ), body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(AppDimensions.pagePadding), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题 Text( '创建账户', style: AppTextStyles.headlineLarge.copyWith( color: AppColors.onSurface, fontWeight: FontWeight.bold, ), ), const SizedBox(height: AppDimensions.spacingSm), Text( '加入AI英语学习平台,开启智能学习之旅', style: AppTextStyles.bodyMedium.copyWith( color: AppColors.onSurfaceVariant, ), ), const SizedBox(height: AppDimensions.spacingXl), // 用户名输入框 CustomTextField( controller: _usernameController, labelText: '用户名', hintText: '请输入用户名', prefixIcon: Icons.person_outline, validator: (value) { if (value == null || value.isEmpty) { return '请输入用户名'; } if (value.length < 3) { return '用户名至少3个字符'; } if (value.length > 20) { return '用户名不能超过20个字符'; } if (!RegExp(r'^[a-zA-Z0-9_]+$').hasMatch(value)) { return '用户名只能包含字母、数字和下划线'; } return null; }, ), const SizedBox(height: AppDimensions.spacingMd), // 昵称输入框 CustomTextField( controller: _nicknameController, labelText: '昵称', hintText: '请输入昵称', prefixIcon: Icons.badge_outlined, validator: (value) { if (value == null || value.isEmpty) { return '请输入昵称'; } if (value.length < 2) { return '昵称至少2个字符'; } if (value.length > 20) { return '昵称不能超过20个字符'; } return null; }, ), const SizedBox(height: AppDimensions.spacingMd), // 邮箱输入框 CustomTextField( controller: _emailController, labelText: '邮箱', hintText: '请输入邮箱地址', prefixIcon: Icons.email_outlined, 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: AppDimensions.spacingMd), // 密码输入框 CustomTextField( controller: _passwordController, labelText: '密码', hintText: '请输入密码', prefixIcon: Icons.lock_outline, obscureText: _obscurePassword, suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_off : Icons.visibility, color: AppColors.onSurfaceVariant, ), onPressed: () { setState(() { _obscurePassword = !_obscurePassword; }); }, ), validator: (value) { if (value == null || value.isEmpty) { return '请输入密码'; } if (value.length < 8) { return '密码至少8个字符'; } // 至少包含一个数字 if (!RegExp(r'[0-9]').hasMatch(value)) { return '密码必须包含数字'; } // 至少包含一个小写字母 if (!RegExp(r'[a-z]').hasMatch(value)) { return '密码必须包含小写字母'; } // 至少包含一个大写字母 if (!RegExp(r'[A-Z]').hasMatch(value)) { return '密码必须包含大写字母'; } // 至少包含一个特殊字符 if (!RegExp(r'[!@#\$%^&*()_+\-=\[\]{};:"\\|,.<>\/?]').hasMatch(value)) { return '密码必须包含特殊字符 (!@#\$%^&*等)'; } return null; }, ), const SizedBox(height: 8), // 密码强度提示 Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey[200]!), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '密码必须包含:', style: AppTextStyles.bodySmall.copyWith( fontWeight: FontWeight.w600, color: Colors.grey[700], ), ), const SizedBox(height: 8), _buildPasswordRequirement('至少8个字符', _hasMinLength), _buildPasswordRequirement('至少一个数字', _hasNumber), _buildPasswordRequirement('至少一个小写字母', _hasLowerCase), _buildPasswordRequirement('至少一个大写字母', _hasUpperCase), _buildPasswordRequirement('至少一个特殊字符 (!@#\$%^&*等)', _hasSpecialChar), ], ), ), const SizedBox(height: AppDimensions.spacingMd), // 确认密码输入框 CustomTextField( controller: _confirmPasswordController, labelText: '确认密码', hintText: '请再次输入密码', prefixIcon: Icons.lock_outline, obscureText: _obscureConfirmPassword, suffixIcon: IconButton( icon: Icon( _obscureConfirmPassword ? Icons.visibility_off : Icons.visibility, color: AppColors.onSurfaceVariant, ), onPressed: () { setState(() { _obscureConfirmPassword = !_obscureConfirmPassword; }); }, ), validator: (value) { if (value == null || value.isEmpty) { return '请确认密码'; } if (value != _passwordController.text) { return '两次输入的密码不一致'; } return null; }, ), const SizedBox(height: AppDimensions.spacingMd), // 同意条款 Row( children: [ Checkbox( value: _agreeToTerms, onChanged: (value) { setState(() { _agreeToTerms = value ?? false; }); }, activeColor: AppColors.primary, ), Expanded( child: GestureDetector( onTap: () { setState(() { _agreeToTerms = !_agreeToTerms; }); }, child: RichText( text: TextSpan( style: AppTextStyles.bodySmall.copyWith( color: AppColors.onSurfaceVariant, ), children: [ const TextSpan(text: '我已阅读并同意'), TextSpan( text: '《用户协议》', style: TextStyle( color: AppColors.primary, decoration: TextDecoration.underline, ), ), const TextSpan(text: '和'), TextSpan( text: '《隐私政策》', style: TextStyle( color: AppColors.primary, decoration: TextDecoration.underline, ), ), ], ), ), ), ), ], ), const SizedBox(height: AppDimensions.spacingLg), // 注册按钮 CustomButton( text: '注册', onPressed: _agreeToTerms ? _handleRegister : null, isLoading: _isLoading, width: double.infinity, ), const SizedBox(height: AppDimensions.spacingMd), // 分割线 Row( children: [ const Expanded(child: Divider(color: AppColors.divider)), Padding( padding: const EdgeInsets.symmetric(horizontal: AppDimensions.buttonPadding), child: Text( '或', style: AppTextStyles.bodySmall.copyWith( color: AppColors.onSurfaceVariant, ), ), ), const Expanded(child: Divider(color: AppColors.divider)), ], ), const SizedBox(height: AppDimensions.spacingMd), // 第三方注册 Row( children: [ Expanded( child: CustomButton( text: '微信注册', onPressed: () => _handleSocialRegister('wechat'), backgroundColor: const Color(0xFF07C160), textColor: Colors.white, icon: Icons.wechat, ), ), const SizedBox(width: AppDimensions.spacingMd), Expanded( child: CustomButton( text: 'QQ注册', onPressed: () => _handleSocialRegister('qq'), backgroundColor: const Color(0xFF12B7F5), textColor: Colors.white, icon: Icons.chat, ), ), ], ), const SizedBox(height: AppDimensions.spacingLg), // 登录链接 Center( child: GestureDetector( onTap: () { Navigator.of(context).pop(); }, child: RichText( text: TextSpan( style: AppTextStyles.bodyMedium.copyWith( color: AppColors.onSurfaceVariant, ), children: [ const TextSpan(text: '已有账户?'), TextSpan( text: '立即登录', style: TextStyle( color: AppColors.primary, fontWeight: FontWeight.w600, ), ), ], ), ), ), ), ], ), ), ), ), ); } /// 构建密码要求项 Widget _buildPasswordRequirement(String text, bool isMet) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: Row( children: [ Icon( isMet ? Icons.check_circle : Icons.circle_outlined, size: 16, color: isMet ? AppColors.success : Colors.grey[400], ), const SizedBox(width: 8), Text( text, style: AppTextStyles.bodySmall.copyWith( color: isMet ? AppColors.success : Colors.grey[600], fontSize: 13, ), ), ], ), ); } /// 处理注册 Future _handleRegister() async { if (!_formKey.currentState!.validate()) { return; } if (!_agreeToTerms) { _showSnackBar('请先同意用户协议和隐私政策'); return; } setState(() { _isLoading = true; }); try { await ref.read(authProvider.notifier).register( username: _usernameController.text.trim(), email: _emailController.text.trim(), password: _passwordController.text, nickname: _nicknameController.text.trim(), ); if (mounted) { _showSnackBar('注册成功,正在自动登录...', isSuccess: true); // 注册成功后自动登录 try { await ref.read(authProvider.notifier).login( account: _usernameController.text.trim(), password: _passwordController.text, ); if (mounted) { // 登录成功,跳转到首页 Navigator.of(context).pushReplacementNamed(Routes.home); } } catch (loginError) { if (mounted) { // 自动登录失败,跳转到登录页 _showSnackBar('注册成功,请登录', isSuccess: true); Future.delayed(const Duration(milliseconds: 1500), () { if (mounted) { Navigator.of(context).pushReplacementNamed(Routes.login); } }); } } } } catch (e) { if (mounted) { _showSnackBar(e.toString()); } } finally { if (mounted) { setState(() { _isLoading = false; }); } } } /// 处理第三方注册 Future _handleSocialRegister(String provider) async { try { // TODO: 实现第三方注册逻辑 _showSnackBar('第三方注册功能即将上线'); } catch (e) { _showSnackBar(e.toString()); } } /// 显示提示信息 void _showSnackBar(String message, {bool isSuccess = false}) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: isSuccess ? AppColors.success : AppColors.error, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), ), ); } }