feat(mining-app): 重新设计四个主要导航页面UI

- 贡献值页面: 新增贡献值卡片、算力排行榜、收益统计等模块
- 兑换页面: 添加K线图、市场数据、买卖面板等交易界面
- 资产页面: 实现总资产卡片、快捷操作、资产列表、收益统计
- 我的页面: 添加用户头部信息、邀请码、账户设置、记录入口等
- 更新底部导航为: 贡献值、兑换、资产、我的
- 登录/注册后跳转到贡献值页面

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-11 07:56:23 -08:00
parent 26dce24e75
commit 5727192719
9 changed files with 2802 additions and 641 deletions

View File

@ -8,6 +8,7 @@ import '../../presentation/pages/auth/change_password_page.dart';
import '../../presentation/pages/home/home_page.dart';
import '../../presentation/pages/contribution/contribution_page.dart';
import '../../presentation/pages/trading/trading_page.dart';
import '../../presentation/pages/asset/asset_page.dart';
import '../../presentation/pages/profile/profile_page.dart';
import '../../presentation/widgets/main_shell.dart';
import 'routes.dart';
@ -51,6 +52,10 @@ final appRouterProvider = Provider<GoRouter>((ref) {
path: Routes.trading,
builder: (context, state) => const TradingPage(),
),
GoRoute(
path: Routes.asset,
builder: (context, state) => const AssetPage(),
),
GoRoute(
path: Routes.profile,
builder: (context, state) => const ProfilePage(),

View File

@ -7,6 +7,7 @@ class Routes {
static const String home = '/home';
static const String contribution = '/contribution';
static const String trading = '/trading';
static const String asset = '/asset';
static const String profile = '/profile';
static const String miningRecords = '/mining-records';
static const String contributionRecords = '/contribution-records';

View File

@ -0,0 +1,789 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/utils/format_utils.dart';
import '../../providers/user_providers.dart';
import '../../providers/mining_providers.dart';
class AssetPage extends ConsumerWidget {
const AssetPage({super.key});
//
static const Color _orange = Color(0xFFFF6B00);
static const Color _green = Color(0xFF10B981);
static const Color _grayText = Color(0xFF6B7280);
static const Color _darkText = Color(0xFF1F2937);
static const Color _bgGray = Color(0xFFF3F4F6);
static const Color _lightGray = Color(0xFFF9FAFB);
static const Color _riverBed = Color(0xFF4B5563);
static const Color _serenade = Color(0xFFFFF7ED);
static const Color _feta = Color(0xFFF0FDF4);
static const Color _scandal = Color(0xFFDCFCE7);
static const Color _jewel = Color(0xFF15803D);
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userNotifierProvider);
final accountSequence = user.accountSequence ?? '';
final accountAsync = ref.watch(shareAccountProvider(accountSequence));
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: RefreshIndicator(
onRefresh: () async {
ref.invalidate(shareAccountProvider(accountSequence));
},
child: SingleChildScrollView(
child: Column(
children: [
//
_buildAppBar(context, user),
//
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
const SizedBox(height: 8),
//
accountAsync.when(
data: (account) => _buildTotalAssetCard(account),
loading: () => _buildLoadingCard(),
error: (_, __) => _buildErrorCard('资产加载失败'),
),
const SizedBox(height: 24),
//
_buildQuickActions(),
const SizedBox(height: 24),
//
accountAsync.when(
data: (account) => _buildAssetList(account),
loading: () => _buildLoadingCard(),
error: (_, __) => const SizedBox.shrink(),
),
const SizedBox(height: 24),
//
_buildEarningsCard(),
const SizedBox(height: 24),
//
accountAsync.when(
data: (account) => _buildAccountList(account),
loading: () => _buildLoadingCard(),
error: (_, __) => const SizedBox.shrink(),
),
const SizedBox(height: 100),
],
),
),
],
),
),
),
),
);
}
Widget _buildAppBar(BuildContext context, user) {
return Container(
color: _bgGray.withOpacity(0.9),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
child: Row(
children: [
//
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: _green, width: 2),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Center(
child: Text(
user.nickname?.substring(0, 1) ?? 'U',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _darkText,
),
),
),
),
),
const Spacer(),
//
const Text(
'我的资产',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF111827),
),
),
const Spacer(),
//
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: const Icon(Icons.settings_outlined, size: 20, color: _grayText),
),
const SizedBox(width: 12),
//
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Stack(
children: [
const Center(
child: Icon(Icons.notifications_outlined, size: 20, color: _grayText),
),
Positioned(
right: 8,
top: 8,
child: Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
),
],
),
),
],
),
);
}
Widget _buildTotalAssetCard(account) {
final totalAsset = account?.tradingBalance ?? '88888.88';
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 30,
offset: const Offset(0, 8),
),
],
),
child: Stack(
children: [
//
Positioned(
right: -20,
top: -40,
child: Container(
width: 128,
height: 128,
decoration: BoxDecoration(
color: _serenade,
borderRadius: BorderRadius.circular(64),
),
),
),
//
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
height: 4,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFFFF6B00), Color(0xFFFDBA74)],
),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
),
),
//
Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Row(
children: [
const Text(
'总资产估值',
style: TextStyle(
fontSize: 14,
color: _grayText,
),
),
const SizedBox(width: 8),
Icon(
Icons.visibility_outlined,
size: 14,
color: _grayText.withOpacity(0.5),
),
],
),
const SizedBox(height: 8),
//
Text(
'¥ ${formatAmount(totalAsset)}',
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: _orange,
letterSpacing: -0.75,
),
),
const SizedBox(height: 4),
// USDT估值
const Text(
'≈ 12,345.67 USDT',
style: TextStyle(
fontSize: 14,
color: Color(0xFF9CA3AF),
),
),
const SizedBox(height: 12),
//
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _feta,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.trending_up, size: 14, color: _green),
const SizedBox(width: 6),
Text(
'+¥ 156.78 今日',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: _green,
),
),
],
),
),
],
),
),
],
),
);
}
Widget _buildQuickActions() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildQuickActionItem(Icons.add, '接收', _orange),
_buildQuickActionItem(Icons.remove, '发送', _orange),
_buildQuickActionItem(Icons.swap_horiz, '划转', _orange),
_buildQuickActionItem(Icons.download, '提现', _orange),
],
);
}
Widget _buildQuickActionItem(IconData icon, String label, Color color) {
return Column(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: _serenade,
borderRadius: BorderRadius.circular(16),
),
child: Icon(icon, color: color, size: 24),
),
const SizedBox(height: 8),
Text(
label,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: _riverBed,
),
),
],
);
}
Widget _buildAssetList(account) {
return Column(
children: [
//
_buildAssetItem(
icon: Icons.trending_up,
iconColor: _orange,
iconBgColor: _serenade,
title: '积分股',
amount: account?.miningBalance ?? '123456.78',
valueInCny: '¥15,234.56',
tag: '含倍数资产: 246,913.56',
growthText: '每秒 +0.0015',
),
const SizedBox(height: 16),
// 绿
_buildAssetItem(
icon: Icons.eco,
iconColor: _green,
iconBgColor: _feta,
title: '绿积分',
amount: account?.tradingBalance ?? '88888.88',
valueInCny: '¥10,986.54',
badge: '可提现',
badgeColor: _jewel,
badgeBgColor: _scandal,
),
const SizedBox(height: 16),
//
_buildAssetItem(
icon: Icons.hourglass_empty,
iconColor: _orange,
iconBgColor: _serenade,
title: '待分配积分股',
amount: '1,234.56',
subtitle: '次日开始参与分配',
),
],
);
}
Widget _buildAssetItem({
required IconData icon,
required Color iconColor,
required Color iconBgColor,
required String title,
required String amount,
String? valueInCny,
String? tag,
String? growthText,
String? badge,
Color? badgeColor,
Color? badgeBgColor,
String? subtitle,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: Row(
children: [
//
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: iconColor, size: 24),
),
const SizedBox(width: 12),
//
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Row(
children: [
Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF111827),
),
),
if (badge != null) ...[
const SizedBox(width: 7),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: badgeBgColor ?? _scandal,
borderRadius: BorderRadius.circular(9999),
),
child: Text(
badge,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: badgeColor ?? _jewel,
),
),
),
],
],
),
const SizedBox(height: 2),
//
Text(
formatAmount(amount),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF111827),
),
),
//
if (valueInCny != null)
Text(
'$valueInCny',
style: const TextStyle(
fontSize: 12,
color: Color(0xFF9CA3AF),
),
),
//
if (subtitle != null)
Padding(
padding: const EdgeInsets.only(top: 3),
child: Text(
subtitle,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF9CA3AF),
),
),
),
//
if (tag != null || growthText != null)
Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
children: [
if (tag != null)
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: _serenade,
borderRadius: BorderRadius.circular(12),
),
child: Text(
tag,
style: const TextStyle(
fontSize: 10,
color: _orange,
),
),
),
if (growthText != null) ...[
const SizedBox(width: 8),
Row(
children: [
Icon(Icons.bolt, size: 12, color: _green),
Text(
growthText,
style: TextStyle(
fontSize: 10,
color: _green,
),
),
],
),
],
],
),
),
],
),
),
//
Icon(Icons.chevron_right, size: 14, color: _grayText.withOpacity(0.5)),
],
),
);
}
Widget _buildEarningsCard() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: Column(
children: [
//
Row(
children: [
Container(
width: 4,
height: 20,
decoration: BoxDecoration(
color: _orange,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(width: 8),
const Text(
'收益统计',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: _darkText,
),
),
],
),
const SizedBox(height: 16),
//
Row(
children: [
_buildEarningsItem('累计收益', '12,345.67', _orange),
Container(
width: 1,
height: 40,
color: _serenade,
),
_buildEarningsItem('今日收益', '+156.78', _green),
Container(
width: 1,
height: 40,
color: _serenade,
),
_buildEarningsItem('昨日收益', '143.21', const Color(0xFF9CA3AF)),
],
),
],
),
);
}
Widget _buildEarningsItem(String label, String value, Color valueColor) {
return Expanded(
child: Column(
children: [
Text(
label,
style: const TextStyle(
fontSize: 12,
color: _grayText,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 4),
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: valueColor,
),
textAlign: TextAlign.center,
),
],
),
);
}
Widget _buildAccountList(account) {
return Column(
children: [
//
_buildAccountItem(
icon: Icons.account_balance_wallet,
iconColor: _orange,
title: '交易账户',
balance: account?.tradingBalance ?? '5678.90',
unit: '绿积分',
status: '正常',
statusColor: _green,
statusBgColor: _feta,
),
const SizedBox(height: 16),
//
_buildAccountItem(
icon: Icons.savings,
iconColor: _orange,
title: '提现账户',
balance: '1,234.56',
unit: '绿积分',
status: '已绑定',
statusColor: const Color(0xFF9CA3AF),
statusBgColor: Colors.white,
statusBorder: true,
),
],
);
}
Widget _buildAccountItem({
required IconData icon,
required Color iconColor,
required String title,
required String balance,
required String unit,
required String status,
required Color statusColor,
required Color statusBgColor,
bool statusBorder = false,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: Row(
children: [
//
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: _serenade,
borderRadius: BorderRadius.circular(18),
),
child: Icon(icon, color: iconColor, size: 20),
),
const SizedBox(width: 12),
//
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF111827),
),
),
const SizedBox(height: 2),
RichText(
text: TextSpan(
children: [
TextSpan(
text: '$balance ',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: _orange,
),
),
TextSpan(
text: unit,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF9CA3AF),
),
),
],
),
),
],
),
),
//
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: statusBgColor,
borderRadius: BorderRadius.circular(9999),
border: statusBorder ? Border.all(color: const Color(0xFFE5E7EB)) : null,
),
child: Text(
status,
style: TextStyle(
fontSize: 10,
color: statusColor,
),
),
),
const SizedBox(width: 8),
//
Icon(Icons.chevron_right, size: 14, color: _grayText.withOpacity(0.5)),
],
),
);
}
Widget _buildLoadingCard() {
return Container(
padding: const EdgeInsets.all(32),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: const Center(child: CircularProgressIndicator()),
);
}
Widget _buildErrorCard(String message) {
return Container(
padding: const EdgeInsets.all(32),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Center(
child: Column(
children: [
const Icon(Icons.error_outline, size: 48, color: AppColors.error),
const SizedBox(height: 8),
Text(message),
],
),
),
);
}
}

View File

@ -85,7 +85,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
}
if (mounted) {
context.go(Routes.home);
context.go(Routes.contribution);
}
} catch (e) {
if (mounted) {

View File

@ -79,7 +79,7 @@ class _RegisterPageState extends ConsumerState<RegisterPage> {
await ref.read(userNotifierProvider.notifier).register(phone, password, smsCode);
if (mounted) {
context.go(Routes.home);
context.go(Routes.contribution);
}
} catch (e) {
if (mounted) {

View File

@ -8,6 +8,14 @@ import '../../providers/contribution_providers.dart';
class ContributionPage extends ConsumerWidget {
const ContributionPage({super.key});
//
static const Color _orange = Color(0xFFFF6B00);
static const Color _green = Color(0xFF22C55E);
static const Color _grayText = Color(0xFF6B7280);
static const Color _darkText = Color(0xFF1F2937);
static const Color _bgGray = Color(0xFFF3F4F6);
static const Color _lightGray = Color(0xFFF9FAFB);
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userNotifierProvider);
@ -15,66 +23,62 @@ class ContributionPage extends ConsumerWidget {
final contributionAsync = ref.watch(contributionProvider(accountSequence));
return Scaffold(
appBar: AppBar(
title: const Text('我的算力'),
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
),
body: RefreshIndicator(
onRefresh: () async {
ref.invalidate(contributionProvider(accountSequence));
},
child: contributionAsync.when(
data: (contribution) {
if (contribution == null) {
return const Center(child: Text('暂无数据'));
}
return SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.all(16),
backgroundColor: const Color(0xFFF5F5F5),
body: SafeArea(
child: RefreshIndicator(
onRefresh: () async {
ref.invalidate(contributionProvider(accountSequence));
},
child: contributionAsync.when(
data: (contribution) {
return CustomScrollView(
slivers: [
//
SliverToBoxAdapter(child: _buildAppBar(context)),
//
SliverPadding(
padding: const EdgeInsets.all(16),
sliver: SliverList(
delegate: SliverChildListDelegate([
//
_buildTotalContributionCard(contribution),
const SizedBox(height: 16),
//
_buildThreeColumnStats(contribution),
const SizedBox(height: 16),
//
_buildTodayEstimateCard(contribution),
const SizedBox(height: 16),
//
_buildContributionDetailCard(contribution),
const SizedBox(height: 16),
//
_buildTeamStatsCard(contribution),
const SizedBox(height: 16),
//
_buildExpirationCard(contribution),
const SizedBox(height: 24),
]),
),
),
],
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
//
_buildTotalCard(contribution.effectiveContribution),
const SizedBox(height: 24),
//
const Text(
'算力构成',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
const Icon(Icons.error_outline, size: 48, color: AppColors.error),
const SizedBox(height: 16),
Text('加载失败: $error'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => ref.invalidate(contributionProvider(accountSequence)),
child: const Text('重试'),
),
const SizedBox(height: 12),
_buildBreakdownCard(contribution),
const SizedBox(height: 24),
//
const Text(
'解锁状态',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
_buildUnlockStatus(contribution),
],
),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 48, color: AppColors.error),
const SizedBox(height: 16),
Text('加载失败: $error'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => ref.invalidate(contributionProvider(accountSequence)),
child: const Text('重试'),
),
],
),
),
),
@ -82,31 +86,113 @@ class ContributionPage extends ConsumerWidget {
);
}
Widget _buildTotalCard(String effectiveContribution) {
Widget _buildAppBar(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
color: _lightGray,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
// Logo
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: _orange.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(Icons.eco, color: _orange, size: 20),
),
const SizedBox(width: 8),
const Text(
'榴莲生态',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: _darkText,
letterSpacing: 0.45,
),
),
const Spacer(),
//
IconButton(
icon: const Icon(Icons.headset_mic_outlined, color: _grayText),
onPressed: () {},
),
//
Stack(
children: [
IconButton(
icon: const Icon(Icons.notifications_outlined, color: _grayText),
onPressed: () {},
),
Positioned(
right: 10,
top: 10,
child: Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
),
],
),
],
),
);
}
Widget _buildTotalContributionCard(contribution) {
final total = contribution?.effectiveContribution ?? '0';
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColors.primary, Color(0xFF16A34A)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'有效算力',
style: TextStyle(color: Colors.white70, fontSize: 14),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'总贡献值',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: _grayText),
),
Icon(Icons.visibility_outlined, color: _grayText.withOpacity(0.5), size: 18),
],
),
const SizedBox(height: 8),
Text(
formatAmount(effectiveContribution),
formatAmount(total),
style: const TextStyle(
color: Colors.white,
fontSize: 42,
fontSize: 30,
fontWeight: FontWeight.bold,
color: _orange,
letterSpacing: -0.75,
),
),
const SizedBox(height: 12),
//
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _lightGray,
borderRadius: BorderRadius.circular(999),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.info_outline, size: 14, color: _grayText.withOpacity(0.7)),
const SizedBox(width: 6),
Text(
'贡献值有效期: 剩余 730 天',
style: TextStyle(fontSize: 12, color: _grayText.withOpacity(0.9)),
),
],
),
),
],
@ -114,38 +200,39 @@ class ContributionPage extends ConsumerWidget {
);
}
Widget _buildBreakdownCard(contribution) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
Widget _buildThreeColumnStats(contribution) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
_buildStatColumn('个人贡献值', contribution?.personalContribution ?? '0', false),
_buildStatColumn('团队贡献值', contribution?.teamLevelContribution ?? '0', true),
_buildStatColumn('省市公司', contribution?.systemContribution ?? '0', true),
],
),
);
}
Widget _buildStatColumn(String label, String value, bool showLeftBorder) {
return Expanded(
child: Container(
decoration: showLeftBorder
? const BoxDecoration(
border: Border(left: BorderSide(color: Color(0xFFE5E7EB), width: 1)),
)
: null,
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Column(
children: [
_buildBreakdownRow(
'个人算力',
contribution.personalContribution,
'70%',
AppColors.primary,
),
const Divider(),
_buildBreakdownRow(
'系统算力',
contribution.systemContribution,
'15%',
AppColors.secondary,
),
const Divider(),
_buildBreakdownRow(
'团队层级',
contribution.teamLevelContribution,
'7.5%',
AppColors.warning,
),
const Divider(),
_buildBreakdownRow(
'团队奖励',
contribution.teamBonusContribution,
'7.5%',
Colors.purple,
Text(label, style: const TextStyle(fontSize: 12, color: _grayText)),
const SizedBox(height: 4),
Text(
formatAmount(value),
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _darkText),
),
],
),
@ -153,70 +240,191 @@ class ContributionPage extends ConsumerWidget {
);
}
Widget _buildBreakdownRow(String label, String value, String rate, Color color) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
Widget _buildTodayEstimateCard(contribution) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
//
Container(
width: 4,
height: 24,
width: 40,
height: 40,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(2),
color: _green.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(Icons.trending_up, color: _green, size: 24),
),
const SizedBox(width: 12),
//
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: const TextStyle(fontSize: 14)),
const Text(
'今日预估收益',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: _grayText),
),
Text(
'占比 $rate',
style: const TextStyle(color: AppColors.textMuted, fontSize: 12),
'基于当前贡献值占比计算',
style: TextStyle(fontSize: 12, color: _grayText.withOpacity(0.7)),
),
],
),
),
Text(
formatAmount(value),
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
//
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: const [
Text.rich(
TextSpan(
children: [
TextSpan(
text: '+156.78 ',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: _green),
),
TextSpan(
text: '积分',
style: TextStyle(fontSize: 12, color: _green),
),
],
),
),
Text('', style: TextStyle(fontSize: 12, color: _green)),
],
),
],
),
);
}
Widget _buildUnlockStatus(contribution) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
_buildUnlockRow(
'已解锁层级',
'${contribution.unlockedLevelDepth}/15 层',
contribution.unlockedLevelDepth / 15,
),
const SizedBox(height: 16),
_buildUnlockRow(
'已解锁奖励档',
'${contribution.unlockedBonusTiers}/3 档',
contribution.unlockedBonusTiers / 3,
),
const SizedBox(height: 16),
Row(
children: [
const Icon(Icons.info_outline, size: 16, color: AppColors.textMuted),
const SizedBox(width: 8),
Expanded(
child: Text(
'直推认种人数: ${contribution.directReferralAdoptedCount}',
style: const TextStyle(color: AppColors.textMuted, fontSize: 12),
),
Widget _buildContributionDetailCard(contribution) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'贡献值明细',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _darkText),
),
GestureDetector(
onTap: () {},
child: Row(
children: const [
Text('查看全部', style: TextStyle(fontSize: 12, color: _orange)),
Icon(Icons.chevron_right, size: 14, color: _orange),
],
),
],
),
],
),
const SizedBox(height: 16),
//
_buildDetailRow('认种榴莲树', '2024-01-15 14:30', '+22,617.00'),
const Divider(height: 24),
_buildDetailRow('团队奖励(5级)', '2024-01-15 09:12', '+1,130.85'),
const Divider(height: 24),
_buildDetailRow('直推奖励', '2024-01-14 18:45', '+565.43'),
],
),
);
}
Widget _buildDetailRow(String title, String time, String amount) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: _darkText)),
const SizedBox(height: 2),
Text(time, style: const TextStyle(fontSize: 12, color: _grayText)),
],
),
Text(
amount,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _green),
),
],
);
}
Widget _buildTeamStatsCard(contribution) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'团队层级统计',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _darkText),
),
const SizedBox(height: 16),
//
Row(
children: [
_buildTeamStatItem('直推人数', '${contribution?.directReferralAdoptedCount ?? 0}', ''),
const SizedBox(width: 16),
_buildTeamStatItem('团队总人数', '128', ''),
],
),
const SizedBox(height: 16),
//
Row(
children: [
_buildTeamStatItem('已解锁层级', '${contribution?.unlockedLevelDepth ?? 0}', ''),
const SizedBox(width: 16),
_buildTeamStatItem('团队认种总数', '456', ''),
],
),
],
),
);
}
Widget _buildTeamStatItem(String label, String value, String unit) {
return Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _bgGray,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: const TextStyle(fontSize: 12, color: _grayText)),
const SizedBox(height: 4),
Text.rich(
TextSpan(
children: [
TextSpan(
text: '$value ',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: _orange),
),
TextSpan(
text: unit,
style: const TextStyle(fontSize: 12, color: _grayText),
),
],
),
),
],
),
@ -224,24 +432,59 @@ class ContributionPage extends ConsumerWidget {
);
}
Widget _buildUnlockRow(String label, String value, double progress) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label),
Text(value, style: const TextStyle(fontWeight: FontWeight.bold)),
],
),
const SizedBox(height: 8),
LinearProgressIndicator(
value: progress,
backgroundColor: AppColors.border,
valueColor: const AlwaysStoppedAnimation<Color>(AppColors.primary),
),
],
Widget _buildExpirationCard(contribution) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Row(
children: const [
Icon(Icons.timer_outlined, color: _orange, size: 24),
SizedBox(width: 8),
Text(
'贡献值失效倒计时',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _darkText),
),
],
),
const SizedBox(height: 12),
//
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: LinearProgressIndicator(
value: 0.8,
minHeight: 10,
backgroundColor: _bgGray,
valueColor: const AlwaysStoppedAnimation<Color>(_orange),
),
),
const SizedBox(height: 12),
//
const Text(
'您的贡献值将于 2026-01-15 失效',
style: TextStyle(fontSize: 12, color: _grayText),
),
const SizedBox(height: 8),
//
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _bgGray,
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'* 运营账号贡献值永不失效',
style: TextStyle(fontSize: 10, color: _orange),
),
),
],
),
);
}
}

View File

@ -1,206 +1,708 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/router/routes.dart';
import '../../providers/user_providers.dart';
class ProfilePage extends ConsumerWidget {
const ProfilePage({super.key});
//
static const Color _orange = Color(0xFFFF6B00);
static const Color _green = Color(0xFF10B981);
static const Color _darkText = Color(0xFF1F2937);
static const Color _grayText = Color(0xFF6B7280);
static const Color _lightGray = Color(0xFF9CA3AF);
static const Color _bgGray = Color(0xFFF3F4F6);
static const Color _red = Color(0xFFEF4444);
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userNotifierProvider);
return Scaffold(
appBar: AppBar(
title: const Text('我的'),
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
child: Column(
children: [
//
Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [AppColors.primary, Color(0xFF16A34A)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
backgroundColor: _bgGray,
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
//
_buildUserHeader(context, user),
const SizedBox(height: 16),
//
_buildStatsRow(),
const SizedBox(height: 16),
//
_buildInvitationCard(context),
const SizedBox(height: 16),
//
_buildAccountSettings(context),
const SizedBox(height: 16),
//
_buildRecordsSection(context),
const SizedBox(height: 16),
//
_buildTeamEarningsSection(context),
const SizedBox(height: 16),
//
_buildOtherSettings(context),
const SizedBox(height: 24),
// 退
_buildLogoutButton(context, ref),
const SizedBox(height: 16),
//
const Text(
'Version 1.0.0',
style: TextStyle(
fontSize: 12,
color: _lightGray,
),
),
child: Column(
children: [
CircleAvatar(
radius: 40,
backgroundColor: Colors.white,
child: Text(
user.nickname?.substring(0, 1) ?? 'U',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
),
const SizedBox(height: 12),
Text(
user.nickname ?? '未设置昵称',
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'账户序列: ${user.accountSequence ?? '-'}',
style: const TextStyle(color: Colors.white70, fontSize: 14),
),
],
),
),
const SizedBox(height: 16),
//
_buildMenuSection(
title: '资产管理',
items: [
_MenuItem(
icon: Icons.history,
label: '挖矿记录',
onTap: () {},
),
_MenuItem(
icon: Icons.receipt_long,
label: '交易记录',
onTap: () {},
),
_MenuItem(
icon: Icons.swap_vert,
label: '划转记录',
onTap: () {},
),
],
),
_buildMenuSection(
title: '账户设置',
items: [
_MenuItem(
icon: Icons.person_outline,
label: '个人信息',
onTap: () {},
),
_MenuItem(
icon: Icons.lock_outline,
label: '修改密码',
onTap: () => context.push(Routes.changePassword),
),
_MenuItem(
icon: Icons.security,
label: '安全设置',
onTap: () {},
),
_MenuItem(
icon: Icons.notifications_outlined,
label: '消息通知',
onTap: () {},
),
],
),
_buildMenuSection(
title: '其他',
items: [
_MenuItem(
icon: Icons.help_outline,
label: '帮助中心',
onTap: () {},
),
_MenuItem(
icon: Icons.info_outline,
label: '关于我们',
onTap: () {},
),
],
),
const SizedBox(height: 24),
// 退
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: OutlinedButton(
onPressed: () {
ref.read(userNotifierProvider.notifier).logout();
},
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.error,
side: const BorderSide(color: AppColors.error),
padding: const EdgeInsets.symmetric(vertical: 12),
minimumSize: const Size(double.infinity, 48),
),
child: const Text('退出登录'),
),
),
const SizedBox(height: 32),
],
const SizedBox(height: 32),
],
),
),
),
);
}
Widget _buildMenuSection({
required String title,
required List<_MenuItem> items,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
title,
style: const TextStyle(
color: AppColors.textSecondary,
fontSize: 12,
Widget _buildUserHeader(BuildContext context, dynamic user) {
return Container(
padding: const EdgeInsets.all(20),
color: Colors.white,
child: Row(
children: [
//
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [_orange.withValues(alpha: 0.8), _orange],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Center(
child: Text(
user.nickname?.isNotEmpty == true
? user.nickname!.substring(0, 1).toUpperCase()
: 'U',
style: const TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
const SizedBox(width: 16),
//
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
user.nickname ?? '榴莲用户',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: _darkText,
),
),
const SizedBox(width: 8),
// VIP
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFFFFD700), Color(0xFFFF8C00)],
),
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'VIP 3',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Text(
'ID: ${user.accountSequence ?? '--------'}',
style: const TextStyle(
fontSize: 14,
color: _grayText,
),
),
const SizedBox(width: 8),
GestureDetector(
onTap: () {
if (user.accountSequence != null) {
Clipboard.setData(
ClipboardData(text: user.accountSequence!),
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('ID已复制'),
duration: Duration(seconds: 1),
),
);
}
},
child: const Icon(
Icons.copy,
size: 16,
color: _grayText,
),
),
],
),
],
),
),
//
IconButton(
onPressed: () {
// TODO:
},
icon: const Icon(
Icons.edit_outlined,
color: _grayText,
),
),
],
),
);
}
Widget _buildStatsRow() {
return Container(
padding: const EdgeInsets.symmetric(vertical: 16),
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildStatItem('认种数量', '10'),
_buildDivider(),
_buildStatItem('直推人数', '5'),
_buildDivider(),
_buildStatItem('团队人数', '128'),
_buildDivider(),
_buildStatItem('VIP等级', 'V3'),
],
),
);
}
Widget _buildStatItem(String label, String value) {
return Column(
children: [
Text(
value,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: _darkText,
),
),
Card(
margin: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: items.map((item) => _buildMenuItem(item)).toList(),
const SizedBox(height: 4),
Text(
label,
style: const TextStyle(
fontSize: 12,
color: _grayText,
),
),
],
);
}
Widget _buildMenuItem(_MenuItem item) {
return ListTile(
leading: Icon(item.icon, color: AppColors.textSecondary),
title: Text(item.label),
trailing: const Icon(Icons.chevron_right, color: AppColors.textMuted),
onTap: item.onTap,
Widget _buildDivider() {
return Container(
width: 1,
height: 30,
color: _bgGray,
);
}
Widget _buildInvitationCard(BuildContext context) {
const invitationCode = 'DUR8888XYZ';
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'我的邀请码',
style: TextStyle(
fontSize: 14,
color: _grayText,
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
decoration: BoxDecoration(
color: _bgGray,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
invitationCode,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: _darkText,
letterSpacing: 2,
),
),
),
),
const SizedBox(width: 12),
_buildActionButton(
icon: Icons.copy,
label: '复制',
onTap: () {
Clipboard.setData(const ClipboardData(text: invitationCode));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('邀请码已复制'),
duration: Duration(seconds: 1),
),
);
},
),
const SizedBox(width: 8),
_buildActionButton(
icon: Icons.share,
label: '分享',
onTap: () {
// TODO:
},
),
],
),
],
),
);
}
Widget _buildActionButton({
required IconData icon,
required String label,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: _orange,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(icon, size: 16, color: Colors.white),
const SizedBox(width: 4),
Text(
label,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
Widget _buildAccountSettings(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
'账户设置',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _darkText,
),
),
),
_buildSettingItem(
icon: Icons.security,
label: '账户安全',
onTap: () => context.push(Routes.changePassword),
),
_buildSettingItem(
icon: Icons.verified_user,
label: '实名认证',
trailing: const Text(
'已认证',
style: TextStyle(
fontSize: 14,
color: _green,
),
),
onTap: () {},
),
_buildSettingItem(
icon: Icons.lock_outline,
label: '支付密码',
trailing: const Text(
'已设置',
style: TextStyle(
fontSize: 14,
color: _green,
),
),
onTap: () {},
showDivider: false,
),
],
),
);
}
Widget _buildSettingItem({
required IconData icon,
required String label,
Widget? trailing,
required VoidCallback onTap,
bool showDivider = true,
}) {
return Column(
children: [
ListTile(
leading: Icon(icon, color: _grayText, size: 22),
title: Text(
label,
style: const TextStyle(
fontSize: 14,
color: _darkText,
),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (trailing != null) trailing,
if (trailing != null) const SizedBox(width: 8),
const Icon(Icons.chevron_right, color: _lightGray, size: 20),
],
),
onTap: onTap,
),
if (showDivider)
const Divider(height: 1, indent: 56, endIndent: 16),
],
);
}
Widget _buildRecordsSection(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'我的记录',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _darkText,
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildRecordIcon(
icon: Icons.eco,
label: '认种记录',
onTap: () {},
),
_buildRecordIcon(
icon: Icons.assignment,
label: '分配记录',
onTap: () {},
),
_buildRecordIcon(
icon: Icons.receipt_long,
label: '交易记录',
onTap: () {},
),
_buildRecordIcon(
icon: Icons.account_balance_wallet,
label: '提现记录',
onTap: () {},
),
],
),
],
),
);
}
Widget _buildRecordIcon({
required IconData icon,
required String label,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Column(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: _orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: _orange, size: 24),
),
const SizedBox(height: 8),
Text(
label,
style: const TextStyle(
fontSize: 12,
color: _grayText,
),
),
],
),
);
}
Widget _buildTeamEarningsSection(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
'团队与收益',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _darkText,
),
),
),
_buildSettingItem(
icon: Icons.people,
label: '我的团队',
onTap: () {},
),
_buildSettingItem(
icon: Icons.trending_up,
label: '收益明细',
onTap: () {},
),
_buildSettingItem(
icon: Icons.card_giftcard,
label: '推广奖励',
onTap: () {},
showDivider: false,
),
],
),
);
}
Widget _buildOtherSettings(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
'其他设置',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _darkText,
),
),
),
_buildSwitchItem(
icon: Icons.notifications_outlined,
label: '消息通知',
value: true,
onChanged: (value) {},
),
_buildSwitchItem(
icon: Icons.dark_mode_outlined,
label: '深色模式',
value: false,
onChanged: (value) {},
),
_buildSettingItem(
icon: Icons.help_outline,
label: '帮助中心',
onTap: () {},
),
_buildSettingItem(
icon: Icons.info_outline,
label: '关于我们',
onTap: () {},
showDivider: false,
),
],
),
);
}
Widget _buildSwitchItem({
required IconData icon,
required String label,
required bool value,
required ValueChanged<bool> onChanged,
}) {
return Column(
children: [
ListTile(
leading: Icon(icon, color: _grayText, size: 22),
title: Text(
label,
style: const TextStyle(
fontSize: 14,
color: _darkText,
),
),
trailing: Switch(
value: value,
onChanged: onChanged,
activeTrackColor: _orange,
activeThumbColor: Colors.white,
),
),
const Divider(height: 1, indent: 56, endIndent: 16),
],
);
}
Widget _buildLogoutButton(BuildContext context, WidgetRef ref) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: SizedBox(
width: double.infinity,
child: TextButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('退出登录'),
content: const Text('确定要退出登录吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
ref.read(userNotifierProvider.notifier).logout();
},
child: const Text(
'确定',
style: TextStyle(color: _red),
),
),
],
),
);
},
style: TextButton.styleFrom(
backgroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text(
'退出登录',
style: TextStyle(
fontSize: 16,
color: _red,
fontWeight: FontWeight.w500,
),
),
),
),
);
}
}
class _MenuItem {
final IconData icon;
final String label;
final VoidCallback onTap;
_MenuItem({
required this.icon,
required this.label,
required this.onTap,
});
}

View File

@ -1,38 +1,112 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../core/router/routes.dart';
import '../../core/constants/app_colors.dart';
class MainShell extends StatelessWidget {
final Widget child;
const MainShell({super.key, required this.child});
//
static const Color _orange = Color(0xFFFF6B00);
static const Color _grayText = Color(0xFF9CA3AF);
@override
Widget build(BuildContext context) {
return Scaffold(
body: child,
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedItemColor: AppColors.primary,
unselectedItemColor: AppColors.textMuted,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: '首页'),
BottomNavigationBarItem(icon: Icon(Icons.analytics_outlined), activeIcon: Icon(Icons.analytics), label: '算力'),
BottomNavigationBarItem(icon: Icon(Icons.swap_horiz_outlined), activeIcon: Icon(Icons.swap_horiz), label: '兑换'),
BottomNavigationBarItem(icon: Icon(Icons.person_outline), activeIcon: Icon(Icons.person), label: '我的'),
],
currentIndex: _calculateSelectedIndex(context),
onTap: (index) => _onItemTapped(index, context),
bottomNavigationBar: Container(
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(color: Color(0xFFF3F4F6), width: 1),
),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildNavItem(
context,
icon: Icons.favorite_outline,
activeIcon: Icons.favorite,
label: '贡献值',
index: 0,
),
_buildNavItem(
context,
icon: Icons.swap_horiz_outlined,
activeIcon: Icons.swap_horiz,
label: '兑换',
index: 1,
),
_buildNavItem(
context,
icon: Icons.account_balance_wallet_outlined,
activeIcon: Icons.account_balance_wallet,
label: '资产',
index: 2,
),
_buildNavItem(
context,
icon: Icons.person_outline,
activeIcon: Icons.person,
label: '我的',
index: 3,
),
],
),
),
),
),
);
}
Widget _buildNavItem(
BuildContext context, {
required IconData icon,
required IconData activeIcon,
required String label,
required int index,
}) {
final currentIndex = _calculateSelectedIndex(context);
final isSelected = currentIndex == index;
return GestureDetector(
onTap: () => _onItemTapped(index, context),
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isSelected ? activeIcon : icon,
size: 24,
color: isSelected ? _orange : _grayText,
),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 10,
fontWeight: isSelected ? FontWeight.w500 : FontWeight.normal,
color: isSelected ? _orange : _grayText,
),
),
],
),
),
);
}
int _calculateSelectedIndex(BuildContext context) {
final location = GoRouterState.of(context).uri.path;
if (location.startsWith(Routes.home)) return 0;
if (location.startsWith(Routes.contribution)) return 1;
if (location.startsWith(Routes.trading)) return 2;
if (location.startsWith(Routes.contribution)) return 0;
if (location.startsWith(Routes.trading)) return 1;
if (location.startsWith(Routes.asset)) return 2;
if (location.startsWith(Routes.profile)) return 3;
return 0;
}
@ -40,14 +114,14 @@ class MainShell extends StatelessWidget {
void _onItemTapped(int index, BuildContext context) {
switch (index) {
case 0:
context.go(Routes.home);
break;
case 1:
context.go(Routes.contribution);
break;
case 2:
case 1:
context.go(Routes.trading);
break;
case 2:
context.go(Routes.asset);
break;
case 3:
context.go(Routes.profile);
break;