Files
xianyan/lib/features/auth/presentation/register_step_password.dart
2026-06-12 22:30:26 +08:00

407 lines
13 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// ============================================================
/// 闲言APP — 注册步骤3设置密码与安全选项
/// 创建时间: 2026-06-08
/// 更新时间: 2026-06-12
/// 作用: 注册流程第三步,设置密码、密保问题、订阅邮件,完成注册
/// 上次更新: 移除协议勾选已移至步骤1
/// ============================================================
import 'package:flutter/cupertino.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_typography.dart';
import '../../../../core/theme/app_radius.dart';
import '../../../../l10n/types/t.dart';
import '../services/user_security_service.dart';
import 'login_form_sections.dart';
/// 注册步骤3 — 设置密码与安全选项
class RegisterStepPassword extends StatelessWidget {
const RegisterStepPassword({
super.key,
required this.username,
required this.email,
required this.passwordController,
required this.confirmPasswordController,
required this.secAnswerController,
required this.obscurePassword,
required this.onToggleObscure,
required this.showSecQuestion,
required this.onToggleSecQuestion,
required this.secQuestions,
required this.selectedSecQuestion,
required this.selectedSecQuestionText,
required this.onSelectSecQuestion,
required this.subscribeEmail,
required this.onToggleSubscribe,
required this.onPrev,
required this.onBack,
required this.onRegister,
required this.isLoading,
required this.canRegister,
required this.ext,
required this.auth,
});
final String username;
final String email;
final TextEditingController passwordController;
final TextEditingController confirmPasswordController;
final TextEditingController secAnswerController;
final bool obscurePassword;
final VoidCallback onToggleObscure;
final bool showSecQuestion;
final VoidCallback onToggleSecQuestion;
final List<SecQuestionItem> secQuestions;
final int? selectedSecQuestion;
final String selectedSecQuestionText;
final VoidCallback onSelectSecQuestion;
final bool subscribeEmail;
final VoidCallback onToggleSubscribe;
final VoidCallback onPrev;
final VoidCallback onBack;
final VoidCallback onRegister;
final bool isLoading;
final bool canRegister;
final AppThemeExtension ext;
final TAuth auth;
@override
Widget build(BuildContext context) {
return Column(
children: [
// 已填写账号信息(点击可修改)
GestureDetector(
onTap: onBack,
child: Container(
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: ext.bgSecondary,
borderRadius: AppRadius.mdBorder,
border: Border.all(color: ext.bgElevated),
),
child: Row(
children: [
Icon(
CupertinoIcons.person_crop_circle_fill,
size: 32,
color: ext.accent,
),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
username,
style: AppTypography.subhead.copyWith(
color: ext.textPrimary,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 2),
Text(
email,
style: AppTypography.caption1.copyWith(
color: ext.textSecondary,
),
),
],
),
),
Icon(CupertinoIcons.pencil, size: 16, color: ext.textHint),
],
),
),
),
const SizedBox(height: AppSpacing.md),
// 密码输入
LabeledInputField(
label: auth.setPassword,
labelIcon: CupertinoIcons.lock,
controller: passwordController,
placeholder: auth.passwordHint,
icon: CupertinoIcons.lock,
ext: ext,
obscureText: obscurePassword,
suffix: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onToggleObscure,
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 44, minHeight: 44),
child: Center(
child: Icon(
obscurePassword
? CupertinoIcons.eye_slash
: CupertinoIcons.eye,
size: 18,
color: ext.textHint,
),
),
),
),
),
const SizedBox(height: AppSpacing.md),
// 确认密码
LabeledInputField(
label: auth.confirmPassword,
labelIcon: CupertinoIcons.lock_shield,
controller: confirmPasswordController,
placeholder: auth.confirmPasswordHint,
icon: CupertinoIcons.lock_shield,
ext: ext,
obscureText: true,
),
const SizedBox(height: AppSpacing.md),
// 密保问题折叠区
_buildSecQuestionToggle(),
if (showSecQuestion) ...[
const SizedBox(height: AppSpacing.sm),
_buildSecQuestionSelector(context),
if (selectedSecQuestion != null) ...[
const SizedBox(height: AppSpacing.sm),
_buildSecAnswerInput(),
],
],
const SizedBox(height: AppSpacing.md),
// 邮件订阅
_buildSubscribeRow(),
const SizedBox(height: AppSpacing.lg),
// 导航按钮
_buildNavButtons(),
],
);
}
/// 密保问题折叠切换
Widget _buildSecQuestionToggle() {
return GestureDetector(
onTap: onToggleSecQuestion,
behavior: HitTestBehavior.opaque,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: ext.bgSecondary,
borderRadius: AppRadius.mdBorder,
border: Border.all(
color: showSecQuestion
? ext.accent.withValues(alpha: 0.4)
: ext.textHint.withValues(alpha: 0.15),
),
),
child: Row(
children: [
Icon(
selectedSecQuestion != null
? CupertinoIcons.checkmark_shield_fill
: CupertinoIcons.shield,
size: 16,
color: showSecQuestion ? ext.accent : ext.textHint,
),
const SizedBox(width: AppSpacing.sm),
Text(
auth.secQuestionOptional,
style: AppTypography.subhead.copyWith(
color: showSecQuestion ? ext.accent : ext.textSecondary,
),
),
const Spacer(),
if (selectedSecQuestion != null) ...[
Icon(
CupertinoIcons.checkmark_circle_fill,
size: 14,
color: ext.accent,
),
const SizedBox(width: 4),
],
AnimatedRotation(
duration: const Duration(milliseconds: 200),
turns: showSecQuestion ? 0.5 : 0,
child: Icon(
CupertinoIcons.chevron_down,
size: 14,
color: ext.textHint,
),
),
],
),
),
);
}
/// 密保问题选择按钮
Widget _buildSecQuestionSelector(BuildContext context) {
return CupertinoButton(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
color: ext.bgSecondary,
borderRadius: AppRadius.mdBorder,
onPressed: secQuestions.isEmpty ? null : onSelectSecQuestion,
child: Row(
children: [
Icon(
CupertinoIcons.question_circle,
size: 16,
color: ext.textSecondary,
),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: Text(
selectedSecQuestionText.isNotEmpty
? selectedSecQuestionText
: auth.selectSecQuestion,
style: AppTypography.subhead.copyWith(
color: selectedSecQuestionText.isNotEmpty
? ext.textPrimary
: ext.textHint,
),
),
),
Icon(CupertinoIcons.chevron_right, size: 14, color: ext.textHint),
],
),
);
}
/// 密保答案输入
Widget _buildSecAnswerInput() {
return Container(
decoration: BoxDecoration(
color: ext.bgSecondary,
borderRadius: AppRadius.mdBorder,
),
child: CupertinoTextField(
controller: secAnswerController,
placeholder: auth.enterSecAnswerHint,
placeholderStyle: AppTypography.body.copyWith(color: ext.textHint),
style: AppTypography.body.copyWith(color: ext.textPrimary),
decoration: null,
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm + 2,
),
maxLength: 50,
),
);
}
/// 邮件订阅行
Widget _buildSubscribeRow() {
return GestureDetector(
onTap: onToggleSubscribe,
behavior: HitTestBehavior.opaque,
child: Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: subscribeEmail ? ext.accent : CupertinoColors.transparent,
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: subscribeEmail ? ext.accent : ext.textHint,
width: 1.5,
),
),
child: subscribeEmail
? Icon(
CupertinoIcons.checkmark,
size: 14,
color: ext.textInverse,
)
: null,
),
const SizedBox(width: AppSpacing.sm),
Text(
auth.subscribeEmail,
style: AppTypography.footnote.copyWith(color: ext.textSecondary),
),
],
),
);
}
/// 导航按钮行(上一步 + 完成注册)
Widget _buildNavButtons() {
return Row(
children: [
// 上一步
Expanded(
child: SizedBox(
height: 50,
child: CupertinoButton(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
color: ext.bgSecondary,
borderRadius: AppRadius.mdBorder,
onPressed: onPrev,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
CupertinoIcons.arrow_left,
size: 16,
color: CupertinoColors.white,
),
const SizedBox(width: 6),
Text(
auth.prevStep,
style: AppTypography.callout.copyWith(
color: ext.textPrimary,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
const SizedBox(width: AppSpacing.sm),
// 完成注册
Expanded(
child: SizedBox(
height: 50,
child: CupertinoButton(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
color: ext.accent,
borderRadius: AppRadius.mdBorder,
onPressed: canRegister ? onRegister : null,
child: isLoading
? const CupertinoActivityIndicator(
color: CupertinoColors.white,
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
CupertinoIcons.checkmark_circle,
size: 16,
color: CupertinoColors.white,
),
const SizedBox(width: 6),
Text(
auth.completeRegister,
style: AppTypography.callout.copyWith(
color: CupertinoColors.white,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
],
);
}
}