This commit is contained in:
sjk
2025-11-17 13:39:05 +08:00
commit d4cfe2b9de
479 changed files with 109324 additions and 0 deletions

View File

@@ -0,0 +1,519 @@
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<RegisterScreen> createState() => _RegisterScreenState();
}
class _RegisterScreenState extends ConsumerState<RegisterScreen> {
final _formKey = GlobalKey<FormState>();
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<void> _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<void> _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),
),
),
);
}
}