import 'package:flutter/material.dart'; import '../../../../app/i18n/app_localizations.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../shared/widgets/genex_button.dart'; /// A1. 手机号注册页 /// /// 手机号输入、获取验证码、设置密码、用户协议勾选 /// 注册成功后后台静默创建MPC钱包 class RegisterPage extends StatefulWidget { final bool isEmail; const RegisterPage({super.key, this.isEmail = false}); @override State createState() => _RegisterPageState(); } class _RegisterPageState extends State { final _accountController = TextEditingController(); final _codeController = TextEditingController(); final _passwordController = TextEditingController(); bool _obscurePassword = true; bool _agreeTerms = false; @override void dispose() { _accountController.dispose(); _codeController.dispose(); _passwordController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20), onPressed: () => Navigator.of(context).pop(), ), ), body: SafeArea( child: SingleChildScrollView( padding: AppSpacing.pagePadding, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 16), Text(context.t('register.title'), style: AppTypography.displayMedium), const SizedBox(height: 8), Text( widget.isEmail ? context.t('register.emailSubtitle') : context.t('register.phoneSubtitle'), style: AppTypography.bodyLarge.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: 40), // Step indicator _buildStepIndicator(), const SizedBox(height: 32), // Account Input (Phone/Email) Text( widget.isEmail ? context.t('register.email') : context.t('register.phone'), style: AppTypography.labelMedium, ), const SizedBox(height: 8), TextField( controller: _accountController, keyboardType: widget.isEmail ? TextInputType.emailAddress : TextInputType.phone, decoration: InputDecoration( hintText: widget.isEmail ? context.t('register.emailHint') : context.t('register.phoneHint'), prefixIcon: Icon( widget.isEmail ? Icons.email_outlined : Icons.phone_android_rounded, color: AppColors.textTertiary, ), ), ), const SizedBox(height: 20), // Verification Code Text(context.t('register.code'), style: AppTypography.labelMedium), const SizedBox(height: 8), Row( children: [ Expanded( child: TextField( controller: _codeController, keyboardType: TextInputType.number, maxLength: 6, decoration: InputDecoration( hintText: context.t('register.codeHint'), counterText: '', prefixIcon: Icon(Icons.shield_outlined, color: AppColors.textTertiary), ), ), ), const SizedBox(width: 12), SizedBox( height: AppSpacing.inputHeight, child: GenexButton( label: context.t('register.getCode'), variant: GenexButtonVariant.secondary, size: GenexButtonSize.medium, fullWidth: false, onPressed: () {}, ), ), ], ), const SizedBox(height: 20), // Password Text(context.t('register.setPassword'), style: AppTypography.labelMedium), const SizedBox(height: 8), TextField( controller: _passwordController, obscureText: _obscurePassword, decoration: InputDecoration( hintText: context.t('register.passwordHint'), prefixIcon: const Icon(Icons.lock_outline_rounded, color: AppColors.textTertiary), suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_off_outlined : Icons.visibility_outlined, color: AppColors.textTertiary, size: 20, ), onPressed: () => setState(() => _obscurePassword = !_obscurePassword), ), ), ), const SizedBox(height: 8), _buildPasswordStrength(), const SizedBox(height: 32), // Terms Agreement Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 20, height: 20, child: Checkbox( value: _agreeTerms, onChanged: (v) => setState(() => _agreeTerms = v ?? false), activeColor: AppColors.primary, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), ), ), const SizedBox(width: 8), Expanded( child: GestureDetector( onTap: () => setState(() => _agreeTerms = !_agreeTerms), child: RichText( text: TextSpan( style: AppTypography.bodySmall, children: [ TextSpan(text: '${context.t('register.agreement')} '), TextSpan( text: context.t('register.userAgreement'), style: AppTypography.bodySmall.copyWith(color: AppColors.primary), ), const TextSpan(text: ' & '), TextSpan( text: context.t('register.privacyPolicy'), style: AppTypography.bodySmall.copyWith(color: AppColors.primary), ), ], ), ), ), ), ], ), const SizedBox(height: 32), // Register Button GenexButton( label: context.t('register.submit'), onPressed: _agreeTerms ? () { Navigator.pushReplacementNamed(context, '/main'); } : null, ), const SizedBox(height: 40), ], ), ), ), ); } Widget _buildStepIndicator() { return Row( children: [ _buildStep(1, context.t('register.stepVerify'), true), _buildStepLine(true), _buildStep(2, context.t('register.stepPassword'), true), _buildStepLine(false), _buildStep(3, context.t('register.stepDone'), false), ], ); } Widget _buildStep(int number, String label, bool active) { return Column( children: [ Container( width: 28, height: 28, decoration: BoxDecoration( color: active ? AppColors.primary : AppColors.gray200, shape: BoxShape.circle, ), child: Center( child: Text( '$number', style: TextStyle( color: active ? Colors.white : AppColors.textTertiary, fontSize: 13, fontWeight: FontWeight.w600, ), ), ), ), const SizedBox(height: 4), Text( label, style: AppTypography.caption.copyWith( color: active ? AppColors.primary : AppColors.textTertiary, ), ), ], ); } Widget _buildStepLine(bool active) { return Expanded( child: Container( height: 2, margin: const EdgeInsets.only(bottom: 18), color: active ? AppColors.primary : AppColors.gray200, ), ); } Widget _buildPasswordStrength() { final password = _passwordController.text; final hasLength = password.length >= 8; final hasLetter = RegExp(r'[a-zA-Z]').hasMatch(password); final hasDigit = RegExp(r'\d').hasMatch(password); return Row( children: [ _buildCheck(context.t('register.rule8chars'), hasLength), const SizedBox(width: 16), _buildCheck(context.t('register.ruleLetter'), hasLetter), const SizedBox(width: 16), _buildCheck(context.t('register.ruleNumber'), hasDigit), ], ); } Widget _buildCheck(String label, bool passed) { return Row( mainAxisSize: MainAxisSize.min, children: [ Icon( passed ? Icons.check_circle_rounded : Icons.circle_outlined, size: 14, color: passed ? AppColors.success : AppColors.textTertiary, ), const SizedBox(width: 4), Text( label, style: AppTypography.caption.copyWith( color: passed ? AppColors.success : AppColors.textTertiary, ), ), ], ); } }