rwadurian/frontend/mobile-app/lib/features/auth/presentation/pages/onboarding_page.dart

514 lines
15 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.

import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../../routes/route_paths.dart';
import '../../../../routes/app_router.dart';
import '../../../../core/di/injection_container.dart';
/// 创建账号页面 - 用户首次进入应用时的引导页面
/// 提供创建钱包和导入助记词两种选项
class OnboardingPage extends ConsumerStatefulWidget {
const OnboardingPage({super.key});
@override
ConsumerState<OnboardingPage> createState() => _OnboardingPageState();
}
class _OnboardingPageState extends ConsumerState<OnboardingPage> {
// 用户协议勾选状态
bool _isAgreed = false;
// 创建钱包加载状态
bool _isCreating = false;
// 钱包是否已创建
bool _isWalletCreated = false;
// 是否正在加载状态
bool _isLoading = true;
// 已创建的钱包数据
String? _mnemonic;
String? _kavaAddress;
String? _dstAddress;
String? _bscAddress;
String? _serialNumber;
String? _referralCode;
@override
void initState() {
super.initState();
_checkWalletStatus();
}
/// 检查钱包是否已创建
Future<void> _checkWalletStatus() async {
try {
final accountService = ref.read(accountServiceProvider);
// 检查是否已创建钱包
final hasAccount = await accountService.hasAccount();
if (hasAccount) {
// 读取已保存的钱包数据
final mnemonic = await accountService.getMnemonic();
final addresses = await accountService.getWalletAddresses();
final sequence = await accountService.getAccountSequence();
final referralCode = await accountService.getReferralCode();
if (mounted) {
setState(() {
_isWalletCreated = true;
_mnemonic = mnemonic;
_kavaAddress = addresses?.kava;
_dstAddress = addresses?.dst;
_bscAddress = addresses?.bsc;
_serialNumber = sequence?.toString();
_referralCode = referralCode;
_isLoading = false;
// 如果钱包已创建,自动勾选协议
_isAgreed = true;
});
}
} else {
if (mounted) {
setState(() {
_isWalletCreated = false;
_isLoading = false;
});
}
}
} catch (e) {
debugPrint('检查钱包状态失败: $e');
if (mounted) {
setState(() {
_isWalletCreated = false;
_isLoading = false;
});
}
}
}
/// 创建钱包并跳转到备份页面
///
/// 调用后端 API 使用 MPC 2-of-3 协议生成钱包地址
Future<void> _createWallet() async {
if (!_isAgreed) {
_showAgreementTip();
return;
}
setState(() {
_isCreating = true;
});
try {
// 获取 AccountService
final accountService = ref.read(accountServiceProvider);
// 调用后端 API 创建账号
debugPrint('开始创建账号...');
final response = await accountService.createAccount();
debugPrint('账号创建成功: 序列号=${response.accountSequence}');
if (!mounted) return;
// MPC 模式下,检查是否有客户端分片数据
// 如果有分片数据,需要提示用户妥善保管
final hasMpcData = response.clientShareData != null &&
response.clientShareData!.isNotEmpty;
// 跳转到钱包创建成功页面
context.push(
RoutePaths.backupMnemonic,
extra: BackupMnemonicParams(
// MPC 模式下助记词为空,显示账号信息即可
mnemonicWords: response.mnemonic?.isNotEmpty == true
? response.mnemonic!.split(' ')
: [], // MPC 模式下为空
kavaAddress: response.walletAddresses.kava,
dstAddress: response.walletAddresses.dst,
bscAddress: response.walletAddresses.bsc,
serialNumber: response.accountSequence.toString(),
referralCode: response.referralCode,
publicKey: response.publicKey,
isMpcMode: hasMpcData, // 标记是否为 MPC 模式
),
);
} catch (e) {
debugPrint('创建账号失败: $e');
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('创建账号失败: ${e.toString().replaceAll('Exception: ', '')}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 5),
),
);
} finally {
if (mounted) {
setState(() => _isCreating = false);
}
}
}
/// 显示需要同意协议的提示
void _showAgreementTip() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('请先阅读并同意用户协议和隐私政策'),
backgroundColor: Color(0xFFD4AF37),
),
);
}
/// 显示用户协议
void _showUserAgreement() {
// TODO: 跳转到用户协议页面
debugPrint('显示用户协议');
}
/// 显示隐私政策
void _showPrivacyPolicy() {
// TODO: 跳转到隐私政策页面
debugPrint('显示隐私政策');
}
/// 导入助记词
void _importMnemonic() {
// TODO: 跳转到导入助记词页面
debugPrint('导入助记词');
}
/// 跳转到备份助记词页面(钱包已创建的情况)
void _goToBackupMnemonic() {
if (_mnemonic == null || _kavaAddress == null || _dstAddress == null ||
_bscAddress == null || _serialNumber == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('钱包数据不完整,请重新创建'),
backgroundColor: Colors.red,
),
);
return;
}
context.push(
RoutePaths.backupMnemonic,
extra: BackupMnemonicParams(
mnemonicWords: _mnemonic!.split(' '),
kavaAddress: _kavaAddress!,
dstAddress: _dstAddress!,
bscAddress: _bscAddress!,
serialNumber: _serialNumber!,
referralCode: _referralCode,
isMpcMode: false,
),
);
}
/// 处理按钮点击
void _handleButtonTap() {
if (_isWalletCreated) {
// 钱包已创建,跳转到备份页面
_goToBackupMnemonic();
} else {
// 钱包未创建,创建新钱包
_createWallet();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
// 渐变背景 - 从浅米色到金黄色
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFFFF5E6), // 浅米色
Color(0xFFFFE4B5), // 金黄色
],
),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
child: Column(
children: [
// 顶部 Logo 和标题区域
_buildHeader(),
const SizedBox(height: 48),
// 创建账户说明区域
_buildDescription(),
const Spacer(),
// 底部操作区域
_buildActionSection(),
const SizedBox(height: 9),
],
),
),
),
),
);
}
/// 构建顶部 Logo 和标题
Widget _buildHeader() {
return Column(
children: [
const SizedBox(height: 32),
// Logo 容器
Container(
width: 100,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: const Center(
child: Text(
'ap',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
color: Color(0xFF2E7D32), // 绿色
),
),
),
),
const SizedBox(height: 24),
// 应用标题
const Text(
'榴莲女皇',
style: TextStyle(
fontSize: 32,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037), // 深棕色
),
),
],
);
}
/// 构建创建账户说明区域
Widget _buildDescription() {
return Column(
children: [
// 标题
const Text(
'创建账户',
style: TextStyle(
fontSize: 22,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
letterSpacing: -0.33,
color: Color(0xFF5D4037),
),
),
const SizedBox(height: 16),
// 说明文字
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Text(
'我们将为你创建三个链的钱包地址KAVA / DST / BSC同时生成唯一序列号用于推荐与权益。',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
height: 1.5,
color: Color(0xFF5D4037),
),
textAlign: TextAlign.center,
),
),
],
);
}
/// 构建底部操作区域
Widget _buildActionSection() {
return Column(
children: [
// 生成钱包按钮
_buildCreateButton(),
const SizedBox(height: 20),
// 用户协议勾选
_buildAgreementCheckbox(),
const SizedBox(height: 24),
// 导入助记词链接
_buildImportLink(),
],
);
}
/// 构建创建钱包按钮
Widget _buildCreateButton() {
// 加载中显示骨架
if (_isLoading) {
return Container(
width: double.infinity,
height: 48,
decoration: BoxDecoration(
color: const Color(0xFFD4AF37).withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
),
);
}
// 根据钱包状态决定按钮文字和行为
final buttonText = _isWalletCreated
? '钱包已创建(点击备份助记词)'
: '生成钱包(创建账户)';
// 钱包已创建时,不需要勾选协议
final isEnabled = _isWalletCreated || _isAgreed;
return GestureDetector(
onTap: (_isCreating || !isEnabled) ? null : _handleButtonTap,
child: Opacity(
opacity: isEnabled ? 1.0 : 0.5,
child: Container(
width: double.infinity,
height: 48,
decoration: BoxDecoration(
color: _isWalletCreated
? const Color(0xFF52C41A) // 绿色表示已创建
: const Color(0xFFD4AF37), // 金色表示待创建
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: _isCreating
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_isWalletCreated) ...[
const Icon(
Icons.check_circle,
color: Colors.white,
size: 20,
),
const SizedBox(width: 8),
],
Text(
buttonText,
style: const TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
letterSpacing: 0.24,
color: Colors.white,
),
),
],
),
),
),
),
);
}
/// 构建用户协议勾选框
Widget _buildAgreementCheckbox() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 勾选框
SizedBox(
width: 20,
height: 20,
child: Checkbox(
value: _isAgreed,
onChanged: (value) {
setState(() {
_isAgreed = value ?? false;
});
},
activeColor: const Color(0xFFD4AF37),
side: const BorderSide(color: Color(0xFF5D4037)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
),
const SizedBox(width: 8),
// 协议文字
Flexible(
child: RichText(
text: TextSpan(
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xFF5D4037),
),
children: [
const TextSpan(text: '我已阅读并同意 '),
TextSpan(
text: '《用户协议》',
style: const TextStyle(color: Color(0xFFD4AF37)),
recognizer: TapGestureRecognizer()..onTap = _showUserAgreement,
),
TextSpan(
text: '《隐私政策》',
style: const TextStyle(color: Color(0xFFD4AF37)),
recognizer: TapGestureRecognizer()..onTap = _showPrivacyPolicy,
),
],
),
),
),
],
);
}
/// 构建导入助记词链接
Widget _buildImportLink() {
return GestureDetector(
onTap: _importMnemonic,
child: const Text(
'已有账号? 导入助记词',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xFFD4AF37),
),
),
);
}
}