import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:formz/formz.dart'; import '../../../../core/theme/design_tokens.dart'; import '../../../../shared/widgets/app_button.dart'; import '../../../../shared/widgets/toast/toast.dart'; import '../../../../shared/widgets/toast/toast_type.dart'; import '../../presentation/cubits/register_cubit.dart'; import '../../presentation/bloc/auth_bloc.dart'; import '../../presentation/bloc/auth_event.dart'; class RegisterVerificationScreen extends StatelessWidget { final RegisterCubit? cubit; const RegisterVerificationScreen({super.key, this.cubit}); @override Widget build(BuildContext context) { final registerCubit = cubit ?? (GoRouterState.of(context).extra as RegisterCubit?); if (registerCubit == null) { return Scaffold( body: Center(child: Text('Error: RegisterCubit not found')), ); } return BlocProvider.value( value: registerCubit, child: const RegisterVerificationView(), ); } } class RegisterVerificationView extends StatefulWidget { const RegisterVerificationView({super.key}); @override State createState() => _RegisterVerificationViewState(); } class _RegisterVerificationViewState extends State { final _codeController = TextEditingController(); Timer? _countdownTimer; int _countdown = 0; bool _firstSendCompleted = false; @override void initState() { super.initState(); } @override void dispose() { _countdownTimer?.cancel(); _codeController.dispose(); super.dispose(); } void _startCountdown() { setState(() { _countdown = 60; }); _countdownTimer?.cancel(); _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { if (_countdown > 0) { setState(() { _countdown--; }); } else { timer.cancel(); } }); } Future _handleComplete() async { final cubit = context.read(); cubit.verificationCodeChanged(_codeController.text); if (!cubit.state.isStep2Valid) { return; } final response = await cubit.submitStep2(); if (response != null && mounted) { context.read().add(AuthLoggedIn(user: response.user)); context.go('/home'); } } Future _handleResendCode() async { final cubit = context.read(); final success = await cubit.resendCode(); if (success && mounted) { _startCountdown(); Toast.show(context, '验证码已发送', type: ToastType.success); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Center( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildAppIcon(), const SizedBox(height: 24), _buildAppTitle(), const SizedBox(height: 24), _buildFormContainer(), ], ), ), ), _buildFooter(), const SizedBox(height: 24), ], ), ), ), ); } Widget _buildAppIcon() { return Container( width: 104, height: 104, decoration: BoxDecoration( color: AppColors.appIconRing, borderRadius: BorderRadius.circular(52), border: Border.all(color: AppColors.appIconBorder, width: 1), ), child: Center( child: ClipRRect( borderRadius: BorderRadius.circular(38), child: Image.asset( 'assets/images/logo.png', width: 76, height: 76, fit: BoxFit.cover, ), ), ), ); } Widget _buildAppTitle() { return const Text( 'linksy', style: TextStyle( fontFamily: 'Playfair Display', fontSize: 34, fontWeight: FontWeight.w700, fontStyle: FontStyle.italic, color: AppColors.appTitle, letterSpacing: 0.5, ), ); } Widget _buildFormContainer() { return BlocConsumer( listener: (context, state) { if (state.status == FormzSubmissionStatus.failure && state.errorMessage != null) { Toast.show(context, state.errorMessage!, type: ToastType.error); if (!_firstSendCompleted) { _firstSendCompleted = true; setState(() { _countdown = 0; }); } } if (state.status == FormzSubmissionStatus.success && !_firstSendCompleted) { _firstSendCompleted = true; _startCountdown(); } }, builder: (context, state) { return SizedBox( width: 327, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildCodeInput(state), const SizedBox(height: 12), _buildStepIndicator(), const SizedBox(height: 12), AppButton( text: '完成注册', onPressed: state.status == FormzSubmissionStatus.inProgress ? null : _handleComplete, ), ], ), ); }, ); } Widget _buildCodeInput(RegisterState state) { final canResend = _countdown == 0 && state.status != FormzSubmissionStatus.inProgress; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '邮箱验证码', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: Color(0xFF475569), ), ), const SizedBox(height: 6), Row( children: [ Expanded( child: SizedBox( height: 40, child: TextField( controller: _codeController, keyboardType: TextInputType.number, decoration: const InputDecoration( hintText: '输入验证码', contentPadding: EdgeInsets.symmetric( horizontal: 12, vertical: 10, ), ), ), ), ), const SizedBox(width: 8), _buildResendButton(canResend, state), ], ), ], ); } Widget _buildResendButton(bool canResend, RegisterState state) { final borderColor = canResend ? AppColors.primary : AppColors.slate300; final textColor = canResend ? AppColors.primary : AppColors.slate400; String text; if (state.status == FormzSubmissionStatus.inProgress) { text = '发送中...'; } else if (canResend) { text = '重新发送'; } else { text = '$_countdown s'; } return SizedBox( width: 90, height: 40, child: OutlinedButton( onPressed: canResend ? _handleResendCode : null, style: OutlinedButton.styleFrom( backgroundColor: AppColors.background, side: BorderSide(color: borderColor), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), child: Text( text, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: textColor, ), ), ), ); } Widget _buildStepIndicator() { return Row( children: [ Expanded( child: Container( height: 4, decoration: BoxDecoration( color: AppColors.primary, borderRadius: BorderRadius.circular(99), ), ), ), const SizedBox(width: 6), Expanded( child: Container( height: 4, decoration: BoxDecoration( color: AppColors.primary, borderRadius: BorderRadius.circular(99), ), ), ), ], ); } Widget _buildFooter() { return GestureDetector( onTap: () => context.pop(), child: const Text( '已有账号?去登录', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.slate500, ), ), ); } }