1904 lines
60 KiB
Dart
1904 lines
60 KiB
Dart
import 'dart:io';
|
||
import 'dart:async';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:flutter_svg/flutter_svg.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:package_info_plus/package_info_plus.dart';
|
||
import 'package:device_info_plus/device_info_plus.dart';
|
||
import '../../../../core/di/injection_container.dart';
|
||
import '../../../../core/services/referral_service.dart';
|
||
import '../../../../routes/route_paths.dart';
|
||
import '../../../../routes/app_router.dart';
|
||
|
||
/// 个人中心页面 - 显示用户信息、社区数据、收益和设置
|
||
/// 包含用户资料、推荐信息、社区考核、收益领取等功能
|
||
class ProfilePage extends ConsumerStatefulWidget {
|
||
const ProfilePage({super.key});
|
||
|
||
@override
|
||
ConsumerState<ProfilePage> createState() => _ProfilePageState();
|
||
}
|
||
|
||
class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||
// 用户数据(从存储/API加载)
|
||
String _nickname = '加载中...';
|
||
String _serialNumber = '--';
|
||
String? _avatarSvg;
|
||
String? _avatarUrl;
|
||
String? _localAvatarPath; // 本地头像文件路径
|
||
String _referrerSerial = '--'; // 推荐人序列号(从API获取)
|
||
String _referralCode = '--'; // 我的推荐码
|
||
|
||
// 授权数据(从 authorization-service 获取)
|
||
String _community = '--';
|
||
String _parentCommunity = '--';
|
||
String _childCommunity = '--';
|
||
String _cityCompany = '--';
|
||
String _provinceCompany = '--';
|
||
String _province = '--';
|
||
|
||
// 团队数据(从 referral-service 获取)
|
||
int _directReferralCount = 0;
|
||
int _totalTeamCount = 0;
|
||
int _personalPlantingCount = 0;
|
||
int _teamPlantingCount = 0;
|
||
double _leaderboardScore = 0;
|
||
int? _leaderboardRank;
|
||
|
||
// 直推数据(从 referral-service 获取)
|
||
List<Map<String, dynamic>> _referrals = [];
|
||
|
||
// 社区考核数据
|
||
final int _communityLevel = 3;
|
||
final int _currentPlanting = 12;
|
||
final int _requiredPlanting = 50;
|
||
|
||
// 收益数据(从 wallet-service 获取)
|
||
double _pendingUsdt = 0.0;
|
||
double _pendingPower = 0.0;
|
||
double _settleableUsdt = 0.0;
|
||
double _settledUsdt = 0.0;
|
||
double _expiredUsdt = 0.0;
|
||
double _expiredPower = 0.0;
|
||
bool _isLoadingWallet = true;
|
||
String? _walletError;
|
||
|
||
// 倒计时
|
||
Timer? _timer;
|
||
int _remainingSeconds = 0;
|
||
|
||
// 应用版本信息
|
||
String _appVersion = '--';
|
||
String _buildNumber = '--';
|
||
String _packageName = '--';
|
||
String _deviceModel = '--';
|
||
String _osVersion = '--';
|
||
String _platform = '--';
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
// 先同步检查本地头像,再异步加载其他数据
|
||
_checkLocalAvatarSync();
|
||
_loadUserData();
|
||
_loadAppInfo();
|
||
// 加载推荐和授权数据
|
||
_loadReferralData();
|
||
_loadAuthorizationData();
|
||
// 加载钱包和收益数据
|
||
_loadWalletData();
|
||
}
|
||
|
||
/// 加载应用信息
|
||
Future<void> _loadAppInfo() async {
|
||
try {
|
||
// 加载包信息
|
||
final packageInfo = await PackageInfo.fromPlatform();
|
||
|
||
// 加载设备信息
|
||
final deviceInfoPlugin = DeviceInfoPlugin();
|
||
String deviceModel = '';
|
||
String osVersion = '';
|
||
String platform = '';
|
||
|
||
if (Platform.isAndroid) {
|
||
final androidInfo = await deviceInfoPlugin.androidInfo;
|
||
deviceModel = '${androidInfo.brand} ${androidInfo.model}';
|
||
osVersion = 'Android ${androidInfo.version.release} (SDK ${androidInfo.version.sdkInt})';
|
||
platform = 'Android';
|
||
} else if (Platform.isIOS) {
|
||
final iosInfo = await deviceInfoPlugin.iosInfo;
|
||
deviceModel = iosInfo.model;
|
||
osVersion = '${iosInfo.systemName} ${iosInfo.systemVersion}';
|
||
platform = 'iOS';
|
||
}
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
_appVersion = packageInfo.version;
|
||
_buildNumber = packageInfo.buildNumber;
|
||
_packageName = packageInfo.packageName;
|
||
_deviceModel = deviceModel;
|
||
_osVersion = osVersion;
|
||
_platform = platform;
|
||
});
|
||
}
|
||
} catch (e) {
|
||
debugPrint('[ProfilePage] 加载应用信息失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 同步检查本地头像文件(在 build 之前快速获取)
|
||
void _checkLocalAvatarSync() {
|
||
// 使用 WidgetsBinding 确保在第一帧渲染前执行
|
||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||
final accountService = ref.read(accountServiceProvider);
|
||
final localPath = await accountService.getLocalAvatarPath();
|
||
if (mounted && localPath != null && _localAvatarPath == null) {
|
||
setState(() {
|
||
_localAvatarPath = localPath;
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
/// 加载用户数据
|
||
Future<void> _loadUserData() async {
|
||
debugPrint('[ProfilePage] _loadUserData() - 开始加载本地存储数据...');
|
||
final accountService = ref.read(accountServiceProvider);
|
||
|
||
// 并行加载本地存储数据
|
||
final results = await Future.wait([
|
||
accountService.getUsername(),
|
||
accountService.getUserSerialNum(),
|
||
accountService.getAvatarSvg(),
|
||
accountService.getAvatarUrl(),
|
||
accountService.getLocalAvatarPath(),
|
||
accountService.getReferralCode(),
|
||
]);
|
||
|
||
final username = results[0] as String?;
|
||
final serialNum = results[1] as int?;
|
||
final avatarSvg = results[2] as String?;
|
||
final avatarUrl = results[3] as String?;
|
||
final localAvatarPath = results[4] as String?;
|
||
final referralCode = results[5] as String?;
|
||
|
||
debugPrint('[ProfilePage] _loadUserData() - 本地存储数据:');
|
||
debugPrint('[ProfilePage] username: $username');
|
||
debugPrint('[ProfilePage] serialNum: $serialNum');
|
||
debugPrint('[ProfilePage] avatarSvg: ${avatarSvg != null ? "长度=${avatarSvg.length}" : "null"}');
|
||
debugPrint('[ProfilePage] avatarUrl: $avatarUrl');
|
||
debugPrint('[ProfilePage] localAvatarPath: $localAvatarPath');
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
_nickname = username ?? '未设置昵称';
|
||
_serialNumber = serialNum?.toString() ?? '--';
|
||
_avatarSvg = avatarSvg;
|
||
_avatarUrl = avatarUrl;
|
||
_localAvatarPath = localAvatarPath;
|
||
_referralCode = referralCode ?? '--';
|
||
});
|
||
|
||
// 如果有远程URL但没有本地缓存,后台下载并缓存
|
||
if (avatarUrl != null && avatarUrl.isNotEmpty && localAvatarPath == null) {
|
||
_downloadAndCacheAvatar(avatarUrl);
|
||
}
|
||
}
|
||
|
||
// 从API获取完整用户信息(包括推荐人)
|
||
_loadMeData();
|
||
}
|
||
|
||
/// 从API加载完整用户信息
|
||
Future<void> _loadMeData() async {
|
||
try {
|
||
debugPrint('[ProfilePage] _loadMeData() - 开始加载用户信息...');
|
||
final accountService = ref.read(accountServiceProvider);
|
||
final meData = await accountService.getMe();
|
||
|
||
debugPrint('[ProfilePage] _loadMeData() - API返回数据:');
|
||
debugPrint('[ProfilePage] nickname: ${meData.nickname}');
|
||
debugPrint('[ProfilePage] avatarUrl: ${meData.avatarUrl != null ? "长度=${meData.avatarUrl!.length}" : "null"}');
|
||
debugPrint('[ProfilePage] avatarUrl内容前100字符: ${meData.avatarUrl?.substring(0, meData.avatarUrl!.length > 100 ? 100 : meData.avatarUrl!.length) ?? "null"}');
|
||
debugPrint('[ProfilePage] 当前_avatarSvg: ${_avatarSvg != null ? "长度=${_avatarSvg!.length}" : "null"}');
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
_referrerSerial = meData.inviterSequence?.toString() ?? '无';
|
||
// 如果API返回了更新的数据,也更新本地显示
|
||
if (meData.nickname.isNotEmpty) {
|
||
_nickname = meData.nickname;
|
||
}
|
||
if (meData.referralCode.isNotEmpty) {
|
||
_referralCode = meData.referralCode;
|
||
}
|
||
// 如果本地没有头像但API返回了头像,更新显示
|
||
debugPrint('[ProfilePage] _loadMeData() - 检查头像同步条件:');
|
||
debugPrint('[ProfilePage] _avatarSvg == null: ${_avatarSvg == null}');
|
||
debugPrint('[ProfilePage] meData.avatarUrl != null: ${meData.avatarUrl != null}');
|
||
debugPrint('[ProfilePage] meData.avatarUrl.isNotEmpty: ${meData.avatarUrl?.isNotEmpty ?? false}');
|
||
|
||
if (_avatarSvg == null && meData.avatarUrl != null && meData.avatarUrl!.isNotEmpty) {
|
||
debugPrint('[ProfilePage] _loadMeData() - 同步头像到本地!');
|
||
_avatarSvg = meData.avatarUrl;
|
||
// 同时保存到本地存储,避免下次还需要从API获取
|
||
accountService.updateLocalAvatarSvg(meData.avatarUrl!);
|
||
} else {
|
||
debugPrint('[ProfilePage] _loadMeData() - 不需要同步头像 (条件不满足)');
|
||
}
|
||
});
|
||
}
|
||
} catch (e, stackTrace) {
|
||
debugPrint('[ProfilePage] 加载用户信息失败: $e');
|
||
debugPrint('[ProfilePage] 堆栈: $stackTrace');
|
||
// 失败时保持现有数据,不影响页面显示
|
||
}
|
||
}
|
||
|
||
/// 加载推荐数据 (from referral-service)
|
||
Future<void> _loadReferralData() async {
|
||
try {
|
||
debugPrint('[ProfilePage] 开始加载推荐数据...');
|
||
final referralService = ref.read(referralServiceProvider);
|
||
|
||
// 并行加载推荐信息和直推列表
|
||
final results = await Future.wait([
|
||
referralService.getMyReferralInfo(),
|
||
referralService.getDirectReferrals(limit: 10),
|
||
]);
|
||
|
||
final referralInfo = results[0] as ReferralInfoResponse;
|
||
final directReferrals = results[1] as DirectReferralsResponse;
|
||
|
||
debugPrint('[ProfilePage] 推荐数据加载成功: directReferralCount=${referralInfo.directReferralCount}, totalTeamCount=${referralInfo.totalTeamCount}');
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
_directReferralCount = referralInfo.directReferralCount;
|
||
_totalTeamCount = referralInfo.totalTeamCount;
|
||
_personalPlantingCount = referralInfo.personalPlantingCount;
|
||
_teamPlantingCount = referralInfo.teamPlantingCount;
|
||
_leaderboardScore = referralInfo.leaderboardScore;
|
||
_leaderboardRank = referralInfo.leaderboardRank;
|
||
|
||
// 转换直推列表格式
|
||
_referrals = directReferrals.referrals.map((r) => <String, dynamic>{
|
||
'serial': r.accountSequence, // 使用8位账户序列号显示
|
||
'personal': r.personalPlantingCount, // 个人认种量
|
||
'team': r.teamPlantingCount, // 团队认种量
|
||
}).toList();
|
||
});
|
||
}
|
||
} catch (e, stackTrace) {
|
||
debugPrint('[ProfilePage] 加载推荐数据失败: $e');
|
||
debugPrint('[ProfilePage] 堆栈: $stackTrace');
|
||
// 失败时保持默认数据
|
||
}
|
||
}
|
||
|
||
/// 加载授权数据 (from authorization-service)
|
||
Future<void> _loadAuthorizationData() async {
|
||
try {
|
||
final authorizationService = ref.read(authorizationServiceProvider);
|
||
final summary = await authorizationService.getMyAuthorizationSummary();
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
_community = summary.communityName ?? '--';
|
||
_cityCompany = summary.cityCompanyName ?? '--';
|
||
_provinceCompany = summary.provinceCompanyName ?? '--';
|
||
// 上级社区和下级社区暂时无法从当前API获取
|
||
// 后续需要扩展API或从推荐链中计算
|
||
});
|
||
}
|
||
} catch (e) {
|
||
debugPrint('[ProfilePage] 加载授权数据失败: $e');
|
||
// 失败时保持默认数据
|
||
}
|
||
}
|
||
|
||
/// 加载钱包和收益数据 (from wallet-service)
|
||
Future<void> _loadWalletData() async {
|
||
try {
|
||
debugPrint('[ProfilePage] ========== 加载钱包数据 ==========');
|
||
debugPrint('[ProfilePage] mounted: $mounted');
|
||
setState(() {
|
||
_isLoadingWallet = true;
|
||
_walletError = null;
|
||
});
|
||
|
||
debugPrint('[ProfilePage] 获取 walletServiceProvider...');
|
||
final walletService = ref.read(walletServiceProvider);
|
||
debugPrint('[ProfilePage] 调用 getMyWallet()...');
|
||
final wallet = await walletService.getMyWallet();
|
||
|
||
debugPrint('[ProfilePage] -------- 钱包数据加载成功 --------');
|
||
debugPrint('[ProfilePage] 待领取 USDT: ${wallet.rewards.pendingUsdt}');
|
||
debugPrint('[ProfilePage] 待领取 算力: ${wallet.rewards.pendingHashpower}');
|
||
debugPrint('[ProfilePage] 可结算 USDT: ${wallet.rewards.settleableUsdt}');
|
||
debugPrint('[ProfilePage] 已结算 USDT: ${wallet.rewards.settledTotalUsdt}');
|
||
debugPrint('[ProfilePage] 已过期 USDT: ${wallet.rewards.expiredTotalUsdt}');
|
||
debugPrint('[ProfilePage] 已过期 算力: ${wallet.rewards.expiredTotalHashpower}');
|
||
debugPrint('[ProfilePage] 过期时间: ${wallet.rewards.pendingExpireAt}');
|
||
debugPrint('[ProfilePage] 剩余秒数: ${wallet.rewards.pendingRemainingSeconds}');
|
||
|
||
if (mounted) {
|
||
debugPrint('[ProfilePage] 更新 UI 状态...');
|
||
setState(() {
|
||
_pendingUsdt = wallet.rewards.pendingUsdt;
|
||
_pendingPower = wallet.rewards.pendingHashpower;
|
||
_settleableUsdt = wallet.rewards.settleableUsdt;
|
||
_settledUsdt = wallet.rewards.settledTotalUsdt;
|
||
_expiredUsdt = wallet.rewards.expiredTotalUsdt;
|
||
_expiredPower = wallet.rewards.expiredTotalHashpower;
|
||
_remainingSeconds = wallet.rewards.pendingRemainingSeconds;
|
||
_isLoadingWallet = false;
|
||
});
|
||
|
||
debugPrint('[ProfilePage] UI 状态更新完成');
|
||
debugPrint('[ProfilePage] _pendingUsdt: $_pendingUsdt');
|
||
debugPrint('[ProfilePage] _remainingSeconds: $_remainingSeconds');
|
||
|
||
// 启动倒计时(如果有待领取收益)
|
||
if (_remainingSeconds > 0) {
|
||
debugPrint('[ProfilePage] 启动倒计时: $_remainingSeconds 秒');
|
||
_startCountdown();
|
||
} else {
|
||
debugPrint('[ProfilePage] 无需启动倒计时(剩余时间为0)');
|
||
}
|
||
} else {
|
||
debugPrint('[ProfilePage] 组件已卸载,跳过 UI 更新');
|
||
}
|
||
debugPrint('[ProfilePage] ================================');
|
||
} catch (e, stackTrace) {
|
||
debugPrint('[ProfilePage] !!!!!!!!!! 加载钱包数据失败 !!!!!!!!!!');
|
||
debugPrint('[ProfilePage] 错误类型: ${e.runtimeType}');
|
||
debugPrint('[ProfilePage] 错误信息: $e');
|
||
debugPrint('[ProfilePage] 堆栈跟踪:');
|
||
debugPrint('$stackTrace');
|
||
if (mounted) {
|
||
setState(() {
|
||
_isLoadingWallet = false;
|
||
_walletError = '加载失败,点击重试';
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 后台下载并缓存头像
|
||
Future<void> _downloadAndCacheAvatar(String url) async {
|
||
final accountService = ref.read(accountServiceProvider);
|
||
final localPath = await accountService.downloadAndCacheAvatar(url);
|
||
if (mounted && localPath != null) {
|
||
setState(() {
|
||
_localAvatarPath = localPath;
|
||
});
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_timer?.cancel();
|
||
super.dispose();
|
||
}
|
||
|
||
/// 开始倒计时
|
||
void _startCountdown() {
|
||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||
if (_remainingSeconds > 0) {
|
||
setState(() {
|
||
_remainingSeconds--;
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
/// 格式化倒计时
|
||
String _formatCountdown() {
|
||
final hours = (_remainingSeconds ~/ 3600).toString().padLeft(2, '0');
|
||
final minutes = ((_remainingSeconds % 3600) ~/ 60).toString().padLeft(2, '0');
|
||
final seconds = (_remainingSeconds % 60).toString().padLeft(2, '0');
|
||
return '$hours : $minutes : $seconds';
|
||
}
|
||
|
||
/// 复制序列号
|
||
void _copySerialNumber() {
|
||
Clipboard.setData(ClipboardData(text: _serialNumber));
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(
|
||
content: Text('序列号已复制'),
|
||
backgroundColor: Color(0xFFD4AF37),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 跳转到分享页面
|
||
void _goToSharePage() {
|
||
context.push(
|
||
RoutePaths.share,
|
||
extra: SharePageParams(
|
||
shareLink: 'https://s3.szaiai.com/rwadurian/app-release.apk',
|
||
referralCode: _referralCode,
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 领取全部收益
|
||
void _claimAllEarnings() {
|
||
// 如果没有待领取收益,提示用户
|
||
if (_pendingUsdt <= 0 && _pendingPower <= 0) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(
|
||
content: Text('暂无可领取的收益'),
|
||
backgroundColor: Color(0xFF8B5A2B),
|
||
),
|
||
);
|
||
return;
|
||
}
|
||
|
||
showDialog(
|
||
context: context,
|
||
builder: (dialogContext) => AlertDialog(
|
||
title: const Text('确认领取'),
|
||
content: Text(
|
||
'确定领取全部收益吗?\nUSDT: ${_formatNumber(_pendingUsdt)}\n算力: ${_formatNumber(_pendingPower)}',
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(dialogContext),
|
||
child: const Text('取消'),
|
||
),
|
||
TextButton(
|
||
onPressed: () async {
|
||
Navigator.pop(dialogContext);
|
||
await _doClaimRewards();
|
||
},
|
||
child: const Text('确认'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 执行领取奖励
|
||
Future<void> _doClaimRewards() async {
|
||
try {
|
||
debugPrint('[ProfilePage] ========== 执行领取奖励 ==========');
|
||
debugPrint('[ProfilePage] 当前待领取: USDT=$_pendingUsdt, 算力=$_pendingPower');
|
||
|
||
final walletService = ref.read(walletServiceProvider);
|
||
debugPrint('[ProfilePage] 调用 claimRewards()...');
|
||
await walletService.claimRewards();
|
||
|
||
debugPrint('[ProfilePage] 领取成功,准备刷新数据');
|
||
if (mounted) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(
|
||
content: Text('领取成功'),
|
||
backgroundColor: Color(0xFFD4AF37),
|
||
),
|
||
);
|
||
// 刷新钱包数据
|
||
debugPrint('[ProfilePage] 刷新钱包数据...');
|
||
_loadWalletData();
|
||
}
|
||
debugPrint('[ProfilePage] ================================');
|
||
} catch (e, stackTrace) {
|
||
debugPrint('[ProfilePage] !!!!!!!!!! 领取奖励失败 !!!!!!!!!!');
|
||
debugPrint('[ProfilePage] 错误: $e');
|
||
debugPrint('[ProfilePage] 堆栈: $stackTrace');
|
||
if (mounted) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(
|
||
content: Text('领取失败: $e'),
|
||
backgroundColor: Colors.red,
|
||
),
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 结算
|
||
void _onSettlement() {
|
||
context.go(RoutePaths.trading);
|
||
}
|
||
|
||
/// 充值USDT
|
||
void _onDeposit() {
|
||
context.push(RoutePaths.depositUsdt);
|
||
}
|
||
|
||
/// 进入交易
|
||
void _goToTrading() {
|
||
context.go(RoutePaths.trading);
|
||
}
|
||
|
||
/// 进入认种
|
||
void _goToPlanting() {
|
||
context.push(RoutePaths.plantingQuantity);
|
||
}
|
||
|
||
/// 谷歌验证器
|
||
void _goToGoogleAuth() {
|
||
context.push(RoutePaths.googleAuth);
|
||
}
|
||
|
||
/// 修改密码
|
||
void _goToChangePassword() {
|
||
context.push(RoutePaths.changePassword);
|
||
}
|
||
|
||
/// 绑定邮箱
|
||
void _goToBindEmail() {
|
||
context.push(RoutePaths.bindEmail);
|
||
}
|
||
|
||
/// 编辑资料
|
||
Future<void> _goToEditProfile() async {
|
||
final result = await context.push<bool>(RoutePaths.editProfile);
|
||
// 如果编辑页面返回 true,说明有更新,刷新用户数据
|
||
if (result == true && mounted) {
|
||
_loadUserData();
|
||
}
|
||
}
|
||
|
||
/// 格式化数字
|
||
String _formatNumber(double number) {
|
||
final parts = number.toStringAsFixed(2).split('.');
|
||
final intPart = parts[0].replaceAllMapped(
|
||
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
|
||
(Match m) => '${m[1]},',
|
||
);
|
||
return '$intPart.${parts[1]}';
|
||
}
|
||
|
||
/// 格式化整数
|
||
String _formatInt(int number) {
|
||
return number.toString().replaceAllMapped(
|
||
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
|
||
(Match m) => '${m[1]},',
|
||
);
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: Colors.white,
|
||
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: SingleChildScrollView(
|
||
child: Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const SizedBox(height: 16),
|
||
// 用户头像和基本信息
|
||
_buildUserHeader(),
|
||
const SizedBox(height: 16),
|
||
// 推荐人信息卡片
|
||
_buildReferralInfoCard(),
|
||
const SizedBox(height: 16),
|
||
// 社区/省份标签
|
||
_buildCommunityLabel(),
|
||
const SizedBox(height: 8),
|
||
// 认种按钮
|
||
_buildPlantingButton(),
|
||
const SizedBox(height: 16),
|
||
// 主要内容卡片
|
||
_buildMainContentCard(),
|
||
const SizedBox(height: 24),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建用户头像和基本信息
|
||
Widget _buildUserHeader() {
|
||
return Row(
|
||
children: [
|
||
// 头像(点击跳转到编辑资料页面)
|
||
GestureDetector(
|
||
onTap: _goToEditProfile,
|
||
child: Container(
|
||
width: 80,
|
||
height: 80,
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(40),
|
||
color: const Color(0xFFFFF5E6),
|
||
),
|
||
child: ClipRRect(
|
||
borderRadius: BorderRadius.circular(40),
|
||
child: _buildAvatarContent(),
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(width: 16),
|
||
// 用户信息
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
_nickname,
|
||
style: const TextStyle(
|
||
fontSize: 20,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
letterSpacing: -0.3,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
const SizedBox(height: 3),
|
||
Row(
|
||
children: [
|
||
Text(
|
||
'序列号: $_serialNumber',
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
const SizedBox(width: 8),
|
||
GestureDetector(
|
||
onTap: _copySerialNumber,
|
||
child: const Icon(
|
||
Icons.copy,
|
||
size: 16,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 构建头像内容(优先本地文件,其次网络URL,最后SVG)
|
||
Widget _buildAvatarContent() {
|
||
debugPrint('[ProfilePage] _buildAvatarContent() - 开始构建头像');
|
||
debugPrint('[ProfilePage] _localAvatarPath: $_localAvatarPath');
|
||
debugPrint('[ProfilePage] _avatarUrl: $_avatarUrl');
|
||
debugPrint('[ProfilePage] _avatarSvg: ${_avatarSvg != null ? "长度=${_avatarSvg!.length}" : "null"}');
|
||
|
||
// 1. 优先显示本地缓存的头像文件
|
||
if (_localAvatarPath != null && _localAvatarPath!.isNotEmpty) {
|
||
final file = File(_localAvatarPath!);
|
||
// 同步检查文件是否存在
|
||
if (file.existsSync()) {
|
||
debugPrint('[ProfilePage] _buildAvatarContent() - 使用本地文件: $_localAvatarPath');
|
||
return Image.file(
|
||
file,
|
||
width: 80,
|
||
height: 80,
|
||
fit: BoxFit.cover,
|
||
gaplessPlayback: true, // 防止图片切换时闪烁
|
||
errorBuilder: (context, error, stackTrace) {
|
||
debugPrint('[ProfilePage] _buildAvatarContent() - 本地文件加载失败: $error');
|
||
// 本地文件加载失败,尝试网络URL
|
||
return _buildNetworkOrSvgAvatar();
|
||
},
|
||
);
|
||
} else {
|
||
debugPrint('[ProfilePage] _buildAvatarContent() - 本地文件不存在');
|
||
}
|
||
}
|
||
|
||
// 2. 没有本地缓存,尝试网络URL或SVG
|
||
return _buildNetworkOrSvgAvatar();
|
||
}
|
||
|
||
/// 构建网络URL或SVG头像
|
||
Widget _buildNetworkOrSvgAvatar() {
|
||
debugPrint('[ProfilePage] _buildNetworkOrSvgAvatar() - 尝试网络URL或SVG');
|
||
// 尝试显示网络图片URL
|
||
if (_avatarUrl != null && _avatarUrl!.isNotEmpty) {
|
||
debugPrint('[ProfilePage] _buildNetworkOrSvgAvatar() - 使用网络URL: $_avatarUrl');
|
||
return Image.network(
|
||
_avatarUrl!,
|
||
width: 80,
|
||
height: 80,
|
||
fit: BoxFit.cover,
|
||
loadingBuilder: (context, child, loadingProgress) {
|
||
if (loadingProgress == null) return child;
|
||
return const Center(
|
||
child: SizedBox(
|
||
width: 24,
|
||
height: 24,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFD4AF37)),
|
||
),
|
||
),
|
||
);
|
||
},
|
||
errorBuilder: (context, error, stackTrace) {
|
||
debugPrint('[ProfilePage] _buildNetworkOrSvgAvatar() - 网络图片加载失败: $error');
|
||
// 加载失败时显示SVG或默认头像
|
||
return _buildSvgOrDefaultAvatar();
|
||
},
|
||
);
|
||
}
|
||
|
||
// 显示SVG或默认头像
|
||
debugPrint('[ProfilePage] _buildNetworkOrSvgAvatar() - 无网络URL,尝试SVG');
|
||
return _buildSvgOrDefaultAvatar();
|
||
}
|
||
|
||
/// 构建SVG或默认头像
|
||
/// 注意:_avatarSvg 可能存储的是 URL(用户上传的图片)或 SVG 字符串(随机生成的头像)
|
||
Widget _buildSvgOrDefaultAvatar() {
|
||
if (_avatarSvg != null && _avatarSvg!.isNotEmpty) {
|
||
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 检查头像内容,长度=${_avatarSvg!.length}');
|
||
debugPrint('[ProfilePage] 内容前50字符: ${_avatarSvg!.substring(0, _avatarSvg!.length > 50 ? 50 : _avatarSvg!.length)}');
|
||
|
||
// 检测是否是 URL(用户上传的头像图片)
|
||
if (_avatarSvg!.startsWith('http://') || _avatarSvg!.startsWith('https://')) {
|
||
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 检测到是URL,使用网络图片');
|
||
return Image.network(
|
||
_avatarSvg!,
|
||
width: 80,
|
||
height: 80,
|
||
fit: BoxFit.cover,
|
||
loadingBuilder: (context, child, loadingProgress) {
|
||
if (loadingProgress == null) return child;
|
||
return const Center(
|
||
child: SizedBox(
|
||
width: 24,
|
||
height: 24,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFD4AF37)),
|
||
),
|
||
),
|
||
);
|
||
},
|
||
errorBuilder: (context, error, stackTrace) {
|
||
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 网络图片加载失败: $error');
|
||
return const Icon(
|
||
Icons.person,
|
||
size: 40,
|
||
color: Color(0xFF8B5A2B),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
// 检测是否是 SVG 字符串(随机生成的 SVG 头像)
|
||
if (_avatarSvg!.contains('<svg') || _avatarSvg!.startsWith('<?xml')) {
|
||
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 检测到是SVG字符串,使用SvgPicture');
|
||
return SvgPicture.string(
|
||
_avatarSvg!,
|
||
width: 80,
|
||
height: 80,
|
||
fit: BoxFit.cover,
|
||
);
|
||
}
|
||
|
||
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 未知格式,使用默认图标');
|
||
}
|
||
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 无头像数据,使用默认图标');
|
||
return const Icon(
|
||
Icons.person,
|
||
size: 40,
|
||
color: Color(0xFF8B5A2B),
|
||
);
|
||
}
|
||
|
||
/// 构建推荐人信息卡片
|
||
Widget _buildReferralInfoCard() {
|
||
return Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x80FFFFFF),
|
||
borderRadius: BorderRadius.circular(12),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// 第一行:推荐人序列号 | 下级社区
|
||
Row(
|
||
children: [
|
||
Expanded(
|
||
child: _buildInfoItem('推荐人的序列号', _referrerSerial),
|
||
),
|
||
Expanded(
|
||
child: _buildInfoItem('下级社区', _childCommunity),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 12),
|
||
// 第二行:所属社区 | 授权市公司
|
||
Row(
|
||
children: [
|
||
Expanded(
|
||
child: _buildInfoItem('所属社区', _community),
|
||
),
|
||
Expanded(
|
||
child: _buildInfoItem('授权市公司', _cityCompany),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 12),
|
||
// 第三行:上级社区 | 授权省公司
|
||
Row(
|
||
children: [
|
||
Expanded(
|
||
child: _buildInfoItem('上级社区', _parentCommunity),
|
||
),
|
||
Expanded(
|
||
child: _buildInfoItem('授权省公司', _provinceCompany),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建信息项(标签 + 值)
|
||
Widget _buildInfoItem(String label, String value) {
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
'$label:',
|
||
style: const TextStyle(
|
||
fontSize: 12,
|
||
fontFamily: 'Inter',
|
||
height: 1.5,
|
||
color: Color(0x995D4037),
|
||
),
|
||
),
|
||
const SizedBox(height: 2),
|
||
Text(
|
||
value,
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w600,
|
||
height: 1.5,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 构建社区/省份标签
|
||
Widget _buildCommunityLabel() {
|
||
return const Text(
|
||
'社区 / 省份',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
height: 1.5,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建认种按钮
|
||
Widget _buildPlantingButton() {
|
||
return GestureDetector(
|
||
onTap: _goToPlanting,
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 48,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFD4AF37),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: const Center(
|
||
child: Text(
|
||
'认种 / Planting',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.24,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建主要内容卡片
|
||
Widget _buildMainContentCard() {
|
||
return Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x80FFFFFF),
|
||
borderRadius: BorderRadius.circular(12),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
children: [
|
||
// 收益区域
|
||
_buildEarningsSection(),
|
||
const SizedBox(height: 16),
|
||
// 结算区域
|
||
_buildSettlementSection(),
|
||
const SizedBox(height: 16),
|
||
// 已过期区域
|
||
_buildExpiredSection(),
|
||
const SizedBox(height: 16),
|
||
// 操作按钮
|
||
_buildActionButtons(),
|
||
const SizedBox(height: 16),
|
||
// 团队统计
|
||
_buildTeamStats(),
|
||
const SizedBox(height: 16),
|
||
// 直推列表
|
||
_buildReferralList(),
|
||
const SizedBox(height: 16),
|
||
// 分享邀请按钮
|
||
_buildShareButton(),
|
||
const SizedBox(height: 16),
|
||
// 社区权益考核
|
||
_buildCommunityAssessment(),
|
||
const SizedBox(height: 16),
|
||
// 设置菜单
|
||
_buildSettingsMenu(),
|
||
const SizedBox(height: 16),
|
||
// 应用版本信息
|
||
_buildAppVersionInfo(),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建收益区域
|
||
Widget _buildEarningsSection() {
|
||
return Container(
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFFFF5E6),
|
||
borderRadius: BorderRadius.circular(8),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: _isLoadingWallet
|
||
? _buildLoadingState()
|
||
: _walletError != null
|
||
? _buildErrorState()
|
||
: _buildEarningsContent(),
|
||
);
|
||
}
|
||
|
||
/// 构建加载状态
|
||
Widget _buildLoadingState() {
|
||
return const SizedBox(
|
||
height: 200,
|
||
child: Center(
|
||
child: CircularProgressIndicator(
|
||
color: Color(0xFFD4AF37),
|
||
strokeWidth: 2,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建错误状态
|
||
Widget _buildErrorState() {
|
||
return GestureDetector(
|
||
onTap: _loadWalletData,
|
||
child: SizedBox(
|
||
height: 200,
|
||
child: Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
const Icon(
|
||
Icons.error_outline,
|
||
color: Color(0xFFE65100),
|
||
size: 40,
|
||
),
|
||
const SizedBox(height: 8),
|
||
Text(
|
||
_walletError!,
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
color: Color(0xFFE65100),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建收益内容
|
||
Widget _buildEarningsContent() {
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// 倒计时
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'24 小时倒计时',
|
||
style: TextStyle(
|
||
fontSize: 12,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
_formatCountdown(),
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Consolas',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
letterSpacing: 0.7,
|
||
color: Color(0xFFD4AF37),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 15),
|
||
// 待领取 USDT
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'待领取 (USDT)',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
_formatNumber(_pendingUsdt),
|
||
style: const TextStyle(
|
||
fontSize: 20,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 11),
|
||
// 待领取 算力
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'待领取 (算力)',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
_formatNumber(_pendingPower),
|
||
style: const TextStyle(
|
||
fontSize: 20,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 15),
|
||
// 领取全部按钮
|
||
GestureDetector(
|
||
onTap: _claimAllEarnings,
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 40,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFD4AF37),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: const Center(
|
||
child: Text(
|
||
'领取全部',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.21,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 构建结算区域
|
||
Widget _buildSettlementSection() {
|
||
return Container(
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFFFF5E6),
|
||
borderRadius: BorderRadius.circular(8),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// 可结算 USDT
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'可结算 (USDT)',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
_formatNumber(_settleableUsdt),
|
||
style: const TextStyle(
|
||
fontSize: 20,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 11),
|
||
// 已结算 USDT
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
const Text(
|
||
'已结算 (USDT)',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
GestureDetector(
|
||
onTap: _onSettlement,
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 7),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFD4AF37),
|
||
borderRadius: BorderRadius.circular(4),
|
||
),
|
||
child: const Text(
|
||
'结算',
|
||
style: TextStyle(
|
||
fontSize: 12,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.18,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 4),
|
||
Text(
|
||
_formatNumber(_settledUsdt),
|
||
style: const TextStyle(
|
||
fontSize: 20,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建已过期区域
|
||
Widget _buildExpiredSection() {
|
||
return SizedBox(
|
||
width: double.infinity,
|
||
child: Container(
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFFFF5E6),
|
||
borderRadius: BorderRadius.circular(8),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'已过期',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
const SizedBox(height: 7),
|
||
// 已过期 USDT
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'已过期 (USDT)',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
_formatNumber(_expiredUsdt),
|
||
style: const TextStyle(
|
||
fontSize: 20,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 11),
|
||
// 已过期 算力
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'已过期 (算力)',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
_formatNumber(_expiredPower),
|
||
style: const TextStyle(
|
||
fontSize: 20,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建操作按钮
|
||
Widget _buildActionButtons() {
|
||
return Column(
|
||
children: [
|
||
// 充值 USDT 按钮
|
||
GestureDetector(
|
||
onTap: _onDeposit,
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 48,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFD4AF37),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: const Center(
|
||
child: Text(
|
||
'充值 USDT (KAVA / BSC)',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.24,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: 12),
|
||
// 进入交易按钮
|
||
GestureDetector(
|
||
onTap: _goToTrading,
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 48,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFF8B5A2B),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: const Center(
|
||
child: Text(
|
||
'进入交易 (卖出 DST → USDT)',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.24,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 构建团队统计
|
||
Widget _buildTeamStats() {
|
||
return Row(
|
||
children: [
|
||
Expanded(
|
||
child: Container(
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFFFF5E6),
|
||
borderRadius: BorderRadius.circular(8),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
children: [
|
||
const Text(
|
||
'直推人数',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
const SizedBox(height: 8),
|
||
Text(
|
||
_formatInt(_directReferralCount),
|
||
style: const TextStyle(
|
||
fontSize: 24,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(width: 16),
|
||
Expanded(
|
||
child: Container(
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFFFF5E6),
|
||
borderRadius: BorderRadius.circular(8),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
children: [
|
||
const Text(
|
||
'团队种植数量',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
const SizedBox(height: 8),
|
||
Text(
|
||
_formatInt(_totalTeamCount),
|
||
style: const TextStyle(
|
||
fontSize: 24,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 构建直推列表
|
||
Widget _buildReferralList() {
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Padding(
|
||
padding: EdgeInsets.only(top: 8),
|
||
child: Text(
|
||
'直推列表',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: 8),
|
||
..._referrals.map((referral) => Padding(
|
||
padding: const EdgeInsets.only(bottom: 4),
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xCCFFF5E6),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
'序列号: ${referral['serial']}',
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
height: 1.43,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
'个人/团队: ${referral['personal']} / ${referral['team']}',
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
height: 1.43,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
)),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 构建分享按钮
|
||
Widget _buildShareButton() {
|
||
return GestureDetector(
|
||
onTap: _goToSharePage,
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 48,
|
||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x33D4AF37),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: const [
|
||
Icon(
|
||
Icons.share,
|
||
size: 24,
|
||
color: Color(0xFFD4AF37),
|
||
),
|
||
SizedBox(width: 8),
|
||
Text(
|
||
'分享邀请',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.24,
|
||
color: Color(0xFFD4AF37),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建社区权益考核
|
||
Widget _buildCommunityAssessment() {
|
||
return Container(
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x80FFFFFF),
|
||
borderRadius: BorderRadius.circular(12),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'社区权益考核',
|
||
style: TextStyle(
|
||
fontSize: 18,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.56,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
const SizedBox(height: 15),
|
||
// 社区等级
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
const Text(
|
||
'社区等级',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.43,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
'Level $_communityLevel',
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.43,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 16),
|
||
// 种植数量
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
const Text(
|
||
'种植数量 / 等级要求',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.43,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Text(
|
||
'$_currentPlanting / $_requiredPlanting',
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.43,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 4),
|
||
// 进度条
|
||
Container(
|
||
height: 6,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x33D4AF37),
|
||
borderRadius: BorderRadius.circular(3),
|
||
),
|
||
child: FractionallySizedBox(
|
||
alignment: Alignment.centerLeft,
|
||
widthFactor: _currentPlanting / _requiredPlanting,
|
||
child: Container(
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFD4AF37),
|
||
borderRadius: BorderRadius.circular(3),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: 16),
|
||
// 社区贡献奖励
|
||
Row(
|
||
children: const [
|
||
Text(
|
||
'社区贡献奖励',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.43,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
SizedBox(width: 39),
|
||
Expanded(
|
||
child: Text(
|
||
'有资格获得 3% 的社区福利',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.43,
|
||
color: Color(0xFFD4AF37),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建设置菜单
|
||
Widget _buildSettingsMenu() {
|
||
return Container(
|
||
padding: const EdgeInsets.all(8),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x80FFFFFF),
|
||
borderRadius: BorderRadius.circular(12),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
children: [
|
||
_buildSettingItem(
|
||
icon: Icons.verified_user,
|
||
title: '谷歌验证器',
|
||
onTap: _goToGoogleAuth,
|
||
),
|
||
_buildSettingItem(
|
||
icon: Icons.lock,
|
||
title: '修改登录密码',
|
||
onTap: _goToChangePassword,
|
||
),
|
||
_buildSettingItem(
|
||
icon: Icons.email,
|
||
title: '绑定邮箱',
|
||
onTap: _goToBindEmail,
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建设置项
|
||
Widget _buildSettingItem({
|
||
required IconData icon,
|
||
required String title,
|
||
required VoidCallback onTap,
|
||
}) {
|
||
return GestureDetector(
|
||
onTap: onTap,
|
||
child: Container(
|
||
height: 48,
|
||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||
child: Row(
|
||
children: [
|
||
Icon(
|
||
icon,
|
||
size: 24,
|
||
color: const Color(0xFF8B5A2B),
|
||
),
|
||
const SizedBox(width: 16),
|
||
Expanded(
|
||
child: Text(
|
||
title,
|
||
style: const TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
),
|
||
const Icon(
|
||
Icons.chevron_right,
|
||
size: 24,
|
||
color: Color(0xFF8B5A2B),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建应用版本信息
|
||
Widget _buildAppVersionInfo() {
|
||
return Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFFFF5E6),
|
||
borderRadius: BorderRadius.circular(8),
|
||
border: Border.all(
|
||
color: const Color(0x33D4AF37),
|
||
width: 1,
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// 标题
|
||
const Row(
|
||
children: [
|
||
Icon(
|
||
Icons.info_outline,
|
||
size: 20,
|
||
color: Color(0xFF8B5A2B),
|
||
),
|
||
SizedBox(width: 8),
|
||
Text(
|
||
'应用信息',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w600,
|
||
height: 1.5,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 12),
|
||
// 版本信息列表
|
||
_buildVersionInfoRow('应用版本', 'v$_appVersion'),
|
||
const SizedBox(height: 8),
|
||
_buildVersionInfoRow('构建号', _buildNumber),
|
||
const SizedBox(height: 8),
|
||
_buildVersionInfoRow('包名', _packageName),
|
||
const Divider(
|
||
height: 24,
|
||
color: Color(0x33D4AF37),
|
||
),
|
||
// 设备信息
|
||
_buildVersionInfoRow('设备型号', _deviceModel),
|
||
const SizedBox(height: 8),
|
||
_buildVersionInfoRow('系统版本', _osVersion),
|
||
const SizedBox(height: 8),
|
||
_buildVersionInfoRow('平台', _platform),
|
||
const SizedBox(height: 12),
|
||
// 版权信息
|
||
Center(
|
||
child: Text(
|
||
'© ${DateTime.now().year} RWADurian. All rights reserved.',
|
||
style: const TextStyle(
|
||
fontSize: 11,
|
||
fontFamily: 'Inter',
|
||
height: 1.5,
|
||
color: Color(0x995D4037),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建版本信息行
|
||
Widget _buildVersionInfoRow(String label, String value) {
|
||
return Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
label,
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
height: 1.5,
|
||
color: Color(0xCC5D4037),
|
||
),
|
||
),
|
||
Flexible(
|
||
child: Text(
|
||
value,
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w500,
|
||
height: 1.5,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
textAlign: TextAlign.right,
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
}
|