feat(mobile-app): 重构向导页5注册流程

1. **推荐码改为必填项**
   - 移除"我没有推荐人"选项
   - 推荐码输入框始终可见
   - 支持手动输入和扫码两种方式
   - 添加推荐码验证逻辑

2. **隐藏导入助记词入口**
   - 保留代码但设置为 if (false) 隐藏
   - 需要时可修改为 true 启用

3. **添加恢复账号入口**
   - 新增"恢复账号(手机号+密码登录)"按钮
   - 点击跳转到 phoneLogin 页面

4. **路由更新**
   - 添加 RoutePaths.phoneLogin 路由常量

UI改进:
- 推荐码输入框样式更新:半透明白色背景
- 扫码按钮图标改为 qr_code_scanner
- 副标题文案改为"请输入推荐码"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-20 19:08:13 -08:00
parent 4ec92d015b
commit 4906fa1815
2 changed files with 97 additions and 200 deletions

View File

@ -283,7 +283,7 @@ class _WelcomePageContent extends ConsumerStatefulWidget {
}
class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
bool _hasReferrer = true;
// _hasReferrer
final TextEditingController _referralCodeController = TextEditingController();
@override
@ -326,12 +326,9 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
return trimmed.length >= 3;
}
///
///
bool get _canProceed {
// "没有推荐人"
if (!_hasReferrer) return true;
// "有推荐人"
//
return _isValidReferralCode(_referralCodeController.text);
}
@ -341,44 +338,19 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
context.push(RoutePaths.importMnemonic);
}
///
Future<void> _goToPhoneRegister() async {
debugPrint('[GuidePage] _goToPhoneRegister - 跳转到手机号注册页面');
//
String? inviterCode;
if (_hasReferrer && _referralCodeController.text.trim().isNotEmpty) {
inviterCode = _referralCodeController.text.trim();
final secureStorage = ref.read(secureStorageProvider);
await secureStorage.write(
key: StorageKeys.inviterReferralCode,
value: inviterCode,
);
debugPrint('[GuidePage] 保存邀请人推荐码: $inviterCode');
}
if (!mounted) return;
context.push(
RoutePaths.phoneRegister,
extra: PhoneRegisterParams(inviterReferralCode: inviterCode),
);
}
/// ()
Future<void> _saveReferralCodeAndProceed() async {
if (!_canProceed) return;
//
String? inviterCode;
if (_hasReferrer && _referralCodeController.text.trim().isNotEmpty) {
inviterCode = _referralCodeController.text.trim();
final secureStorage = ref.read(secureStorageProvider);
await secureStorage.write(
key: StorageKeys.inviterReferralCode,
value: inviterCode,
);
debugPrint('[GuidePage] 保存邀请人推荐码: $inviterCode');
}
//
final inviterCode = _referralCodeController.text.trim();
final secureStorage = ref.read(secureStorageProvider);
await secureStorage.write(
key: StorageKeys.inviterReferralCode,
value: inviterCode,
);
debugPrint('[GuidePage] 保存邀请人推荐码: $inviterCode');
if (!mounted) return;
@ -401,7 +373,6 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
final referralCode = _extractReferralCode(result);
setState(() {
_referralCodeController.text = referralCode;
_hasReferrer = true;
});
}
}
@ -545,7 +516,7 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
SizedBox(height: 12.h),
//
Text(
'创建账号前的最后一步 · 请选择是否有推荐人',
'创建账号前的最后一步 · 请输入推荐码',
style: TextStyle(
fontSize: 14.sp,
height: 1.43,
@ -606,122 +577,57 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
);
}
/// ()
/// ()
Widget _buildReferrerOptions() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
GestureDetector(
onTap: () {
setState(() {
_hasReferrer = true;
});
},
// -
Container(
padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 16.w),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: Colors.white.withValues(alpha: 0.3),
width: 1,
),
),
child: Row(
children: [
_buildRadio(_hasReferrer),
SizedBox(width: 12.w),
Text(
'我有推荐人',
style: TextStyle(
fontSize: 16.sp,
height: 1.5,
color: Colors.white,
shadows: [
Shadow(
color: Colors.black.withValues(alpha: 0.5),
blurRadius: 4,
Expanded(
child: TextField(
controller: _referralCodeController,
decoration: InputDecoration(
hintText: '请输入推荐码或点击右侧扫码',
hintStyle: TextStyle(
fontSize: 15.sp,
color: Colors.white.withValues(alpha: 0.5),
),
],
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.zero,
),
style: TextStyle(
fontSize: 16.sp,
color: Colors.white,
),
),
),
],
),
),
SizedBox(height: 14.h),
// - 使 AnimatedSize
AnimatedSize(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
child: _hasReferrer
? Container(
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
//
GestureDetector(
onTap: _openQrScanner,
child: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1,
color: Colors.white.withValues(alpha: 0.5),
),
),
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8.r),
),
child: Row(
children: [
Expanded(
child: GestureDetector(
onTap: _openQrScanner,
child: AbsorbPointer(
child: TextField(
controller: _referralCodeController,
readOnly: true,
decoration: InputDecoration(
hintText: '点击扫描推荐码',
hintStyle: TextStyle(
fontSize: 16.sp,
color: Colors.black.withValues(alpha: 0.4),
),
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.zero,
),
style: TextStyle(
fontSize: 16.sp,
color: Colors.black,
),
),
),
),
),
//
GestureDetector(
onTap: _openQrScanner,
child: Padding(
padding: EdgeInsets.only(left: 8.w),
child: Icon(
Icons.camera_alt_outlined,
size: 20.sp,
color: Colors.white.withValues(alpha: 0.8),
),
),
),
],
child: Icon(
Icons.qr_code_scanner,
size: 20.sp,
color: Colors.white,
),
)
: const SizedBox.shrink(),
),
SizedBox(height: 24.h),
//
GestureDetector(
onTap: () {
setState(() {
_hasReferrer = false;
});
},
child: Row(
children: [
_buildRadio(!_hasReferrer),
SizedBox(width: 12.w),
Text(
'我没有推荐人',
style: TextStyle(
fontSize: 16.sp,
height: 1.5,
color: Colors.white,
shadows: [
Shadow(
color: Colors.black.withValues(alpha: 0.5),
blurRadius: 4,
),
],
),
),
],
@ -740,7 +646,7 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Text(
'',
'已有账号',
style: TextStyle(
fontSize: 14.sp,
color: Colors.white.withValues(alpha: 0.7),
@ -756,9 +662,12 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
],
),
SizedBox(height: 24.h),
//
// +
GestureDetector(
onTap: _goToPhoneRegister,
onTap: () {
debugPrint('[GuidePage] 跳转到恢复账号页面');
context.push(RoutePaths.phoneLogin);
},
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 14.h),
@ -774,13 +683,13 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.phone_android,
Icons.login,
size: 20.sp,
color: Colors.white,
),
SizedBox(width: 8.w),
Text(
'使用手机号注册',
'恢复账号(手机号+密码登录)',
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w500,
@ -791,62 +700,49 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
),
),
),
SizedBox(height: 12.h),
//
GestureDetector(
onTap: _importMnemonic,
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 14.h),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: Colors.white.withValues(alpha: 0.3),
width: 1,
// -
if (false) // false true
Padding(
padding: EdgeInsets.only(top: 12.h),
child: GestureDetector(
onTap: _importMnemonic,
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 14.h),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: Colors.white.withValues(alpha: 0.3),
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.restore,
size: 20.sp,
color: Colors.white,
),
SizedBox(width: 8.w),
Text(
'已有账号?导入助记词恢复',
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.restore,
size: 20.sp,
color: Colors.white,
),
SizedBox(width: 8.w),
Text(
'已有账号?导入助记词恢复',
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
),
),
),
],
);
}
/// ()
Widget _buildRadio(bool isSelected) {
return Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: isSelected ? 6.w : 2.w,
color: isSelected
? Colors.white
: Colors.white.withValues(alpha: 0.5),
),
),
);
}
}
///

View File

@ -12,6 +12,7 @@ class RoutePaths {
static const importWallet = '/auth/import';
static const importMnemonic = '/auth/import-mnemonic';
static const phoneRegister = '/auth/phone-register';
static const phoneLogin = '/auth/phone-login';
static const smsVerify = '/auth/sms-verify';
static const setPassword = '/auth/set-password';