feat(ui): 优化所有页面使用现代设计系统

- 使用 AppSpacing 替换硬编码间距
- 使用 AppRadius 替换硬编码圆角
- 使用 withValues(alpha:) 替换 withOpacity
- 优化 login_page, home_page, market_page, trade_page, asset_page, mine_page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-23 15:37:59 +08:00
parent 51b2f30a1b
commit b40c9f8360
7 changed files with 172 additions and 162 deletions

View File

@@ -3,6 +3,8 @@ import 'package:flutter/services.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../providers/asset_provider.dart';
import '../../shared/ui_constants.dart';
import '../orders/fund_orders_page.dart';
@@ -45,17 +47,17 @@ class _AssetPageState extends State<AssetPage> with AutomaticKeepAliveClientMixi
color: theme.colorScheme.primary,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.all(16),
padding: AppSpacing.pagePadding,
child: Column(
children: [
_AssetCard(overview: provider.overview),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_TabSelector(
tabs: const ['资金账户', '交易账户'],
selectedIndex: _activeTab,
onChanged: (index) => setState(() => _activeTab = index),
),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_activeTab == 0
? _FundAccountCard(provider: provider)
: _TradeAccountCard(holdings: provider.holdings),
@@ -81,14 +83,14 @@ class _AssetCard extends StatelessWidget {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
padding: EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: AppColors.gradientColors,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(AppRadius.xl),
),
child: Column(
children: [
@@ -96,7 +98,7 @@ class _AssetCard extends StatelessWidget {
'总资产估值(USDT)',
style: theme.textTheme.small.copyWith(color: Colors.white70),
),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
Text(
overview?.totalAsset ?? '0.00',
style: theme.textTheme.h1.copyWith(
@@ -104,12 +106,12 @@ class _AssetCard extends StatelessWidget {
color: Colors.white,
),
),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.trendingUp, color: Colors.white70, size: 16),
const SizedBox(width: 4),
SizedBox(width: AppSpacing.xs),
Text(
'总盈亏: ${overview?.totalProfit ?? '0.00'} USDT',
style: theme.textTheme.small.copyWith(color: Colors.white70),
@@ -139,10 +141,10 @@ class _TabSelector extends StatelessWidget {
final theme = ShadTheme.of(context);
return Container(
padding: const EdgeInsets.all(4),
padding: EdgeInsets.all(AppSpacing.xs),
decoration: BoxDecoration(
color: theme.colorScheme.card,
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(AppRadius.lg),
),
child: Row(
children: tabs.asMap().entries.map((entry) {
@@ -154,10 +156,10 @@ class _TabSelector extends StatelessWidget {
child: GestureDetector(
onTap: () => onChanged(index),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm + AppSpacing.xs),
decoration: BoxDecoration(
color: isSelected ? theme.colorScheme.primary : Colors.transparent,
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Center(
child: Text(
@@ -189,7 +191,7 @@ class _FundAccountCard extends StatelessWidget {
final fund = provider.fundAccount;
return ShadCard(
padding: const EdgeInsets.all(20),
padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.xs),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -211,15 +213,15 @@ class _FundAccountCard extends StatelessWidget {
),
],
),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
Text(fund?.balance ?? '0.00', style: theme.textTheme.h2.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(height: 24),
SizedBox(height: AppSpacing.lg),
Row(
children: [
Expanded(child: _ActionButton(label: '充值', icon: LucideIcons.plus, color: AppColors.deposit, onTap: () => _showDepositDialog(context))),
const SizedBox(width: 12),
Expanded(child: _ActionButton(label: '提现', icon: LucideIcons.minus, color: AppColors.withdraw, onTap: () => _showWithdrawDialog(context, fund?.balance))),
const SizedBox(width: 12),
Expanded(child: _ActionButton(label: '充值', icon: LucideIcons.plus, color: AppColorScheme.success, onTap: () => _showDepositDialog(context))),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Expanded(child: _ActionButton(label: '提现', icon: LucideIcons.minus, color: AppColorScheme.warning, onTap: () => _showWithdrawDialog(context, fund?.balance))),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Expanded(child: _ActionButton.outline(label: '划转', icon: LucideIcons.arrowRightLeft, onTap: () => _showTransferDialog(context))),
],
),
@@ -256,7 +258,7 @@ class _ActionButton extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 18, color: isOutline ? null : Colors.white),
const SizedBox(width: 4),
SizedBox(width: AppSpacing.xs),
Text(label),
],
);
@@ -278,12 +280,12 @@ class _TradeAccountCard extends StatelessWidget {
final theme = ShadTheme.of(context);
return ShadCard(
padding: const EdgeInsets.all(16),
padding: AppSpacing.cardPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('持仓列表', style: theme.textTheme.large.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
if (holdings.isEmpty)
const _EmptyState(icon: LucideIcons.wallet, message: '暂无持仓')
else
@@ -313,11 +315,11 @@ class _EmptyState extends StatelessWidget {
return Center(
child: Padding(
padding: const EdgeInsets.all(32),
padding: EdgeInsets.all(AppSpacing.xl),
child: Column(
children: [
Icon(icon, size: 48, color: theme.colorScheme.mutedForeground),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
Text(message, style: theme.textTheme.muted),
],
),
@@ -337,7 +339,7 @@ class _HoldingItem extends StatelessWidget {
final theme = ShadTheme.of(context);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm),
child: Row(
children: [
CircleAvatar(
@@ -348,7 +350,7 @@ class _HoldingItem extends StatelessWidget {
style: TextStyle(color: theme.colorScheme.primary, fontWeight: FontWeight.bold),
),
),
const SizedBox(width: 12),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -365,7 +367,7 @@ class _HoldingItem extends StatelessWidget {
Text(
holding.formattedProfitRate,
style: TextStyle(
color: holding.isProfit ? AppColors.up : AppColors.down,
color: holding.isProfit ? AppColorScheme.up : AppColorScheme.down,
fontSize: 12,
),
),
@@ -434,13 +436,13 @@ void _showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('订单号: $orderNo', style: const TextStyle(fontSize: 12)),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
Text('充值金额: $amount USDT', style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
const Text('请向以下地址转账:', style: TextStyle(fontSize: 12)),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
_WalletAddressCard(address: walletAddress, network: walletNetwork),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
const Text('转账完成后请点击"已打款"按钮确认', style: TextStyle(fontSize: 12, color: Colors.orange)),
],
),
@@ -474,10 +476,10 @@ class _WalletAddressCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
padding: EdgeInsets.all(AppSpacing.sm + AppSpacing.xs),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -499,7 +501,7 @@ class _WalletAddressCard extends StatelessWidget {
),
],
),
const SizedBox(height: 4),
SizedBox(height: AppSpacing.xs),
Text('网络: $network', style: const TextStyle(fontSize: 12, color: Colors.grey)),
],
),
@@ -526,7 +528,7 @@ void _showWithdrawDialog(BuildContext context, String? balance) {
children: [
if (balance != null)
Padding(
padding: const EdgeInsets.only(bottom: 12),
padding: EdgeInsets.only(bottom: AppSpacing.sm + AppSpacing.xs),
child: Text('可用余额: $balance USDT', style: const TextStyle(color: Colors.grey)),
),
ShadInputFormField(
@@ -536,14 +538,14 @@ void _showWithdrawDialog(BuildContext context, String? balance) {
keyboardType: const TextInputType.numberWithOptions(decimal: true),
validator: Validators.amount,
),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
ShadInputFormField(
id: 'address',
controller: addressController,
placeholder: const Text('请输入提现地址'),
validator: (v) => Validators.required(v, '提现地址'),
),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
ShadInputFormField(
id: 'contact',
controller: contactController,
@@ -593,7 +595,7 @@ void _showTransferDialog(BuildContext context) {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@@ -602,7 +604,7 @@ void _showTransferDialog(BuildContext context) {
isSelected: direction == 1,
onTap: () => setState(() => direction = 1),
),
const SizedBox(width: 8),
SizedBox(width: AppSpacing.sm),
_DirectionButton(
label: '交易→资金',
isSelected: direction == 2,
@@ -610,7 +612,7 @@ void _showTransferDialog(BuildContext context) {
),
],
),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
ShadForm(
key: formKey,
child: ShadInputFormField(
@@ -661,7 +663,7 @@ class _DirectionButton extends StatelessWidget {
child: Row(
children: [
if (isSelected) const Icon(LucideIcons.check, size: 14) else const SizedBox(width: 14),
const SizedBox(width: 4),
SizedBox(width: AppSpacing.xs),
Text(label),
],
),

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../providers/auth_provider.dart';
import '../main/main_page.dart';
import 'register_page.dart';
@@ -29,7 +30,7 @@ class _LoginPageState extends State<LoginPage> {
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: _maxFormWidth),
child: Padding(
padding: const EdgeInsets.all(24),
padding: EdgeInsets.all(AppSpacing.lg),
child: ShadForm(
key: formKey,
child: Column(
@@ -37,13 +38,13 @@ class _LoginPageState extends State<LoginPage> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildHeader(theme),
const SizedBox(height: 48),
SizedBox(height: AppSpacing.xxl),
_buildUsernameField(),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_buildPasswordField(),
const SizedBox(height: 24),
SizedBox(height: AppSpacing.lg),
_buildLoginButton(),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_buildRegisterLink(theme),
],
),
@@ -62,13 +63,13 @@ class _LoginPageState extends State<LoginPage> {
size: _logoSize,
color: theme.colorScheme.primary,
),
const SizedBox(height: 24),
SizedBox(height: AppSpacing.lg),
Text(
'模拟所',
style: theme.textTheme.h1,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
Text(
'虚拟货币模拟交易平台',
style: theme.textTheme.muted,

View File

@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../providers/asset_provider.dart';
import '../../../providers/auth_provider.dart';
import '../../shared/ui_constants.dart';
@@ -44,22 +46,22 @@ class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin
color: theme.colorScheme.primary,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.all(16),
padding: AppSpacing.pagePadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
_Header(),
const SizedBox(height: 20),
SizedBox(height: AppSpacing.lg + AppSpacing.sm),
_AssetOverviewCard(overview: provider.overview),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_QuickActions(
onDeposit: _showDeposit,
onWithdraw: _showWithdraw,
onTransfer: _showTransfer,
onTrade: _navigateToTrade,
),
const SizedBox(height: 24),
SizedBox(height: AppSpacing.lg),
_HoldingsList(holdings: provider.holdings),
],
),
@@ -96,14 +98,14 @@ class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin
keyboardType: const TextInputType.numberWithOptions(decimal: true),
validator: Validators.amount,
),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
ShadInputFormField(
id: 'address',
placeholder: const Text('请输入提现地址'),
controller: addressController,
validator: (v) => Validators.required(v, '提现地址'),
),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
ShadInputFormField(
id: 'contact',
placeholder: const Text('联系方式(可选)'),
@@ -187,7 +189,7 @@ class _Header extends StatelessWidget {
return Row(
children: [
_Avatar(text: user?.avatarText),
const SizedBox(width: 12),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -196,7 +198,7 @@ class _Header extends StatelessWidget {
'你好,${user?.username ?? '用户'}',
style: theme.textTheme.large.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 2),
SizedBox(height: AppSpacing.xs),
Text('欢迎来到模拟所', style: theme.textTheme.muted),
],
),
@@ -241,25 +243,25 @@ class _AssetOverviewCard extends StatelessWidget {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.sm),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: AppColors.gradientColors,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.circular(AppRadius.xl),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('总资产(USDT)', style: theme.textTheme.small.copyWith(color: Colors.white70)),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
Text(
overview?.totalAsset ?? '0.00',
style: theme.textTheme.h2.copyWith(color: Colors.white, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
SizedBox(height: AppSpacing.lg + AppSpacing.sm),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -286,7 +288,7 @@ class _AssetItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: const TextStyle(fontSize: 12, color: Colors.white70)),
const SizedBox(height: 4),
SizedBox(height: AppSpacing.xs),
Text(value, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white)),
],
);
@@ -310,14 +312,14 @@ class _QuickActions extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ShadCard(
padding: const EdgeInsets.all(16),
padding: AppSpacing.cardPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_ActionButton(icon: LucideIcons.arrowDownToLine, text: '充值', color: AppColors.deposit, onTap: onDeposit),
_ActionButton(icon: LucideIcons.arrowUpFromLine, text: '提现', color: AppColors.withdraw, onTap: onWithdraw),
_ActionButton(icon: LucideIcons.arrowRightLeft, text: '划转', color: AppColors.trade, onTap: onTransfer),
_ActionButton(icon: LucideIcons.trendingUp, text: '交易', color: AppColors.trade, onTap: onTrade),
_ActionButton(icon: LucideIcons.arrowDownToLine, text: '充值', color: AppColorScheme.success, onTap: onDeposit),
_ActionButton(icon: LucideIcons.arrowUpFromLine, text: '提现', color: AppColorScheme.warning, onTap: onWithdraw),
_ActionButton(icon: LucideIcons.arrowRightLeft, text: '划转', color: AppColorScheme.info, onTap: onTransfer),
_ActionButton(icon: LucideIcons.trendingUp, text: '交易', color: AppColorScheme.info, onTap: onTrade),
],
),
).animate().fadeIn(duration: 500.ms, delay: 100.ms);
@@ -349,10 +351,10 @@ class _ActionButton extends StatelessWidget {
Container(
width: 48,
height: 48,
decoration: BoxDecoration(color: color.withOpacity(0.15), shape: BoxShape.circle),
decoration: BoxDecoration(color: color.withValues(alpha: 0.15), shape: BoxShape.circle),
child: Icon(icon, color: color, size: 22),
),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
Text(text, style: TextStyle(fontSize: 12, color: theme.colorScheme.foreground)),
],
),
@@ -371,7 +373,7 @@ class _HoldingsList extends StatelessWidget {
final theme = ShadTheme.of(context);
return ShadCard(
padding: const EdgeInsets.all(16),
padding: AppSpacing.cardPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -382,7 +384,7 @@ class _HoldingsList extends StatelessWidget {
Icon(LucideIcons.chevronRight, color: theme.colorScheme.mutedForeground, size: 20),
],
),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
if (holdings.isEmpty)
_EmptyHoldings()
else
@@ -401,13 +403,13 @@ class _EmptyHoldings extends StatelessWidget {
return Center(
child: Padding(
padding: const EdgeInsets.all(32),
padding: EdgeInsets.all(AppSpacing.xl),
child: Column(
children: [
Icon(LucideIcons.wallet, size: 48, color: theme.colorScheme.mutedForeground),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
Text('暂无持仓', style: theme.textTheme.muted),
const SizedBox(height: 4),
SizedBox(height: AppSpacing.xs),
Text('快去交易吧~', style: theme.textTheme.muted.copyWith(fontSize: 12)),
],
),
@@ -452,7 +454,7 @@ class _HoldingItem extends StatelessWidget {
final theme = ShadTheme.of(context);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -466,7 +468,7 @@ class _HoldingItem extends StatelessWidget {
style: TextStyle(color: theme.colorScheme.primary, fontWeight: FontWeight.bold),
),
),
const SizedBox(width: 12),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -483,7 +485,7 @@ class _HoldingItem extends StatelessWidget {
Text(
holding.formattedProfitRate,
style: TextStyle(
color: holding.isProfit ? AppColors.up : AppColors.down,
color: holding.isProfit ? AppColorScheme.up : AppColorScheme.down,
fontSize: 12,
),
),

View File

@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../data/models/coin.dart';
import '../../../providers/market_provider.dart';
@@ -60,7 +62,7 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
final theme = ShadTheme.of(context);
return Padding(
padding: const EdgeInsets.all(16),
padding: AppSpacing.pagePadding,
child: ShadInput(
controller: _searchController,
placeholder: const Text('搜索币种...'),
@@ -98,7 +100,7 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
return Container(
height: 44,
margin: const EdgeInsets.fromLTRB(16, 0, 16, 16),
margin: EdgeInsets.fromLTRB(AppSpacing.md, 0, AppSpacing.md, AppSpacing.md),
child: Row(
children: tabs.asMap().entries.map((entry) {
final index = entry.key;
@@ -108,13 +110,13 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
return GestureDetector(
onTap: () => provider.setTab(tab['key']!),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
margin: const EdgeInsets.only(right: 8),
padding: EdgeInsets.symmetric(horizontal: AppSpacing.lg + AppSpacing.xs, vertical: AppSpacing.sm + AppSpacing.xs),
margin: EdgeInsets.only(right: AppSpacing.sm),
decoration: BoxDecoration(
color: isActive
? theme.colorScheme.primary
: theme.colorScheme.card,
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(AppRadius.xl),
),
child: Text(
tab['label']!,
@@ -153,12 +155,12 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
size: 48,
color: theme.colorScheme.destructive,
),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
Text(
provider.error!,
style: TextStyle(color: theme.colorScheme.destructive),
),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
ShadButton(
onPressed: provider.loadCoins,
child: const Text('重试'),
@@ -179,7 +181,7 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
size: 48,
color: theme.colorScheme.mutedForeground,
),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
Text(
'暂无数据',
style: theme.textTheme.muted,
@@ -193,7 +195,7 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
onRefresh: provider.refresh,
color: theme.colorScheme.primary,
child: ListView.builder(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
padding: EdgeInsets.fromLTRB(AppSpacing.md, 0, AppSpacing.md, AppSpacing.md),
itemCount: coins.length,
itemBuilder: (context, index) => _buildCoinItem(coins[index]),
),
@@ -204,9 +206,9 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
final theme = ShadTheme.of(context);
return Padding(
padding: const EdgeInsets.only(bottom: 8),
padding: EdgeInsets.only(bottom: AppSpacing.sm),
child: ShadCard(
padding: const EdgeInsets.all(16),
padding: AppSpacing.cardPadding,
child: Row(
children: [
// 图标
@@ -221,7 +223,7 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
),
),
),
const SizedBox(width: 12),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
// 名称
Expanded(
child: Column(
@@ -242,15 +244,15 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
),
// 涨跌幅
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
padding: EdgeInsets.symmetric(horizontal: AppSpacing.sm + AppSpacing.xs, vertical: AppSpacing.xs + AppSpacing.xs),
decoration: BoxDecoration(
color: AppColors.getChangeBackgroundColor(coin.isUp),
borderRadius: BorderRadius.circular(6),
color: getChangeBackgroundColor(coin.isUp),
borderRadius: BorderRadius.circular(AppRadius.sm + AppSpacing.xs),
),
child: Text(
coin.formattedChange,
style: TextStyle(
color: AppColors.getChangeColor(coin.isUp),
color: getChangeColor(coin.isUp),
fontWeight: FontWeight.w600,
),
),

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../providers/auth_provider.dart';
import '../../../providers/theme_provider.dart';
import '../auth/login_page.dart';
@@ -40,13 +42,13 @@ class _MinePageState extends State<MinePage> with AutomaticKeepAliveClientMixin
body: Consumer<AuthProvider>(
builder: (context, auth, _) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
padding: AppSpacing.pagePadding,
child: Column(
children: [
_UserCard(user: auth.user),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_MenuList(onShowComingSoon: _showComingSoon, onShowAbout: _showAboutDialog),
const SizedBox(height: 24),
SizedBox(height: AppSpacing.lg),
_LogoutButton(onLogout: () => _handleLogout(auth)),
],
),
@@ -60,11 +62,11 @@ class _MinePageState extends State<MinePage> with AutomaticKeepAliveClientMixin
showShadDialog(
context: context,
builder: (context) => ShadDialog.alert(
title: const Row(
title: Row(
children: [
Icon(LucideIcons.construction, color: Color(0xFFFF9800), size: 20),
SizedBox(width: 8),
Text('功能开发中'),
const Icon(LucideIcons.construction, color: Color(0xFFFF9800), size: 20),
SizedBox(width: AppSpacing.sm),
const Text('功能开发中'),
],
),
description: Text('$feature功能正在开发中,敬请期待~'),
@@ -87,7 +89,7 @@ class _MinePageState extends State<MinePage> with AutomaticKeepAliveClientMixin
title: Row(
children: [
_AppLogo(radius: 20, fontSize: 20),
const SizedBox(width: 12),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
const Text('模拟所'),
],
),
@@ -96,9 +98,9 @@ class _MinePageState extends State<MinePage> with AutomaticKeepAliveClientMixin
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('虚拟货币模拟交易平台', style: theme.textTheme.muted),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_InfoRow(icon: LucideIcons.code, text: '版本: 1.0.0'),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
const _InfoRow(icon: LucideIcons.heart, text: 'Built with Flutter & shadcn_ui'),
],
),
@@ -153,11 +155,11 @@ class _UserCard extends StatelessWidget {
final theme = ShadTheme.of(context);
return ShadCard(
padding: const EdgeInsets.all(20),
padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.sm),
child: Row(
children: [
_AppLogo(radius: 32, fontSize: 24, text: user?.avatarText),
const SizedBox(width: 16),
SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -166,7 +168,7 @@ class _UserCard extends StatelessWidget {
user?.username ?? '未登录',
style: theme.textTheme.h3.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 6),
SizedBox(height: AppSpacing.sm - AppSpacing.xs),
ShadBadge(
backgroundColor: theme.colorScheme.primary.withValues(alpha: 0.2),
child: Text(
@@ -225,7 +227,7 @@ class _InfoRow extends StatelessWidget {
return Row(
children: [
Icon(icon, size: 14, color: theme.colorScheme.mutedForeground),
const SizedBox(width: 6),
SizedBox(width: AppSpacing.sm - AppSpacing.xs),
Text(text, style: theme.textTheme.muted.copyWith(fontSize: 12)),
],
);
@@ -287,17 +289,17 @@ class _ThemeToggleTile extends StatelessWidget {
return InkWell(
onTap: () => themeProvider.toggleTheme(),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: AppSpacing.sm + AppSpacing.xs),
child: Row(
children: [
_MenuIcon(icon: isDarkMode ? LucideIcons.moon : LucideIcons.sun),
const SizedBox(width: 12),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('深色模式', style: theme.textTheme.small.copyWith(fontWeight: FontWeight.w500)),
const SizedBox(height: 2),
SizedBox(height: AppSpacing.xs / 2),
Text(
isDarkMode ? '当前:深色主题' : '当前:浅色主题',
style: theme.textTheme.muted.copyWith(fontSize: 11),
@@ -331,18 +333,18 @@ class _MenuItemTile extends StatelessWidget {
return InkWell(
onTap: item.onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: AppSpacing.sm + AppSpacing.xs),
child: Row(
children: [
_MenuIcon(icon: item.icon),
const SizedBox(width: 12),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(item.title, style: theme.textTheme.small.copyWith(fontWeight: FontWeight.w500)),
if (item.subtitle != null) ...[
const SizedBox(height: 2),
SizedBox(height: AppSpacing.xs / 2),
Text(item.subtitle!, style: theme.textTheme.muted.copyWith(fontSize: 11)),
],
],
@@ -371,7 +373,7 @@ class _MenuIcon extends StatelessWidget {
height: 40,
decoration: BoxDecoration(
color: theme.colorScheme.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(AppRadius.md + AppSpacing.xs),
),
child: Icon(icon, size: 20, color: theme.colorScheme.primary),
);
@@ -391,12 +393,12 @@ class _LogoutButton extends StatelessWidget {
height: 48,
child: ShadButton.destructive(
onPressed: onLogout,
child: const Row(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.logOut, size: 18),
SizedBox(width: 8),
Text('退出登录', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
const Icon(LucideIcons.logOut, size: 18),
SizedBox(width: AppSpacing.sm),
const Text('退出登录', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
],
),
),

View File

@@ -343,19 +343,19 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
}
void _confirmPay(OrderFund order) async {
final confirmed = await showDialog<bool>(
final confirmed = await showShadDialog<bool>(
context: context,
builder: (context) => AlertDialog(
builder: (context) => ShadDialog.alert(
title: const Text('确认已打款'),
content: const Text('确认您已完成向指定地址的转账?'),
description: const Text('确认您已完成向指定地址的转账?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
ShadButton.outline(
child: const Text('取消'),
onPressed: () => Navigator.pop(context, false),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
ShadButton(
child: const Text('确认'),
onPressed: () => Navigator.pop(context, true),
),
],
),
@@ -372,20 +372,19 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
}
void _cancelOrder(OrderFund order) async {
final confirmed = await showDialog<bool>(
final confirmed = await showShadDialog<bool>(
context: context,
builder: (context) => AlertDialog(
builder: (context) => ShadDialog.alert(
title: const Text('取消订单'),
content: Text('确定要取消订单 ${order.orderNo} 吗?'),
description: Text('确定要取消订单 ${order.orderNo} 吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
ShadButton.outline(
child: const Text('返回'),
onPressed: () => Navigator.pop(context, false),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
style: TextButton.styleFrom(foregroundColor: downColor),
ShadButton.destructive(
child: const Text('确定取消'),
onPressed: () => Navigator.pop(context, true),
),
],
),

View File

@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../data/models/coin.dart';
import '../../../providers/market_provider.dart';
import '../../../providers/asset_provider.dart';
@@ -52,7 +54,7 @@ class _TradePageState extends State<TradePage> with AutomaticKeepAliveClientMixi
body: Consumer2<MarketProvider, AssetProvider>(
builder: (context, market, asset, _) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
padding: AppSpacing.pagePadding,
child: ShadForm(
key: _formKey,
child: Column(
@@ -65,9 +67,9 @@ class _TradePageState extends State<TradePage> with AutomaticKeepAliveClientMixi
_priceController.text = coin.formattedPrice;
},
),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
if (_selectedCoin != null) _PriceCard(coin: _selectedCoin!),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_TradeForm(
tradeType: _tradeType,
selectedCoin: _selectedCoin,
@@ -76,7 +78,7 @@ class _TradePageState extends State<TradePage> with AutomaticKeepAliveClientMixi
tradeBalance: asset.overview?.tradeBalance,
onTradeTypeChanged: (type) => setState(() => _tradeType = type),
),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
_TradeButton(
isBuy: _tradeType == 0,
coinCode: _selectedCoin?.code,
@@ -129,7 +131,7 @@ class _TradePageState extends State<TradePage> with AutomaticKeepAliveClientMixi
title: Row(
children: [
Icon(LucideIcons.circleCheck, color: theme.colorScheme.primary, size: 24),
const SizedBox(width: 8),
SizedBox(width: AppSpacing.sm),
const Text('交易成功'),
],
),
@@ -170,11 +172,11 @@ class _CoinSelector extends StatelessWidget {
}
return ShadCard(
padding: const EdgeInsets.all(16),
padding: AppSpacing.cardPadding,
child: Row(
children: [
_CoinAvatar(icon: selectedCoin?.displayIcon),
const SizedBox(width: 12),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -183,7 +185,7 @@ class _CoinSelector extends StatelessWidget {
selectedCoin != null ? '${selectedCoin!.code}/USDT' : '选择币种',
style: theme.textTheme.large.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
SizedBox(height: AppSpacing.xs),
Text(selectedCoin?.name ?? '点击选择交易对', style: theme.textTheme.muted),
],
),
@@ -225,10 +227,10 @@ class _PriceCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
final color = coin.isUp ? AppColors.up : AppColors.down;
final color = coin.isUp ? AppColorScheme.up : AppColorScheme.down;
return ShadCard(
padding: const EdgeInsets.all(16),
padding: AppSpacing.cardPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -236,15 +238,15 @@ class _PriceCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('最新价', style: theme.textTheme.muted),
const SizedBox(height: 4),
SizedBox(height: AppSpacing.xs),
Text('\$${coin.formattedPrice}', style: theme.textTheme.h2.copyWith(fontWeight: FontWeight.bold)),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
padding: EdgeInsets.symmetric(horizontal: AppSpacing.sm + AppSpacing.xs, vertical: AppSpacing.xs + AppSpacing.xs),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Text(
coin.formattedChange,
@@ -280,7 +282,7 @@ class _TradeForm extends StatelessWidget {
final theme = ShadTheme.of(context);
return ShadCard(
padding: const EdgeInsets.all(16),
padding: AppSpacing.cardPadding,
child: Column(
children: [
// 买入/卖出切换
@@ -288,7 +290,7 @@ class _TradeForm extends StatelessWidget {
tradeType: tradeType,
onChanged: onTradeTypeChanged,
),
const SizedBox(height: 20),
SizedBox(height: AppSpacing.lg + AppSpacing.xs),
// 价格输入
ShadInputFormField(
id: 'price',
@@ -296,13 +298,13 @@ class _TradeForm extends StatelessWidget {
controller: priceController,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
placeholder: const Text('输入价格'),
trailing: const Padding(
padding: EdgeInsets.only(right: 8),
child: Text('USDT'),
trailing: Padding(
padding: EdgeInsets.only(right: AppSpacing.sm),
child: const Text('USDT'),
),
validator: Validators.price,
),
const SizedBox(height: 12),
SizedBox(height: AppSpacing.md),
// 数量输入
ShadInputFormField(
id: 'quantity',
@@ -311,15 +313,15 @@ class _TradeForm extends StatelessWidget {
keyboardType: const TextInputType.numberWithOptions(decimal: true),
placeholder: const Text('输入数量'),
trailing: Padding(
padding: const EdgeInsets.only(right: 8),
padding: EdgeInsets.only(right: AppSpacing.sm),
child: Text(selectedCoin?.code ?? ''),
),
validator: Validators.quantity,
),
const SizedBox(height: 16),
SizedBox(height: AppSpacing.md),
// 交易金额
_InfoRow(label: '交易金额', value: '${_calculateAmount()} USDT'),
const SizedBox(height: 8),
SizedBox(height: AppSpacing.sm),
// 可用余额
_InfoRow(label: '可用', value: '${tradeBalance ?? '0.00'} USDT'),
],
@@ -349,16 +351,16 @@ class _TradeTypeSelector extends StatelessWidget {
child: _TypeButton(
label: '买入',
isSelected: tradeType == 0,
color: AppColors.up,
color: AppColorScheme.up,
onTap: () => onChanged(0),
),
),
const SizedBox(width: 16),
SizedBox(width: AppSpacing.md),
Expanded(
child: _TypeButton(
label: '卖出',
isSelected: tradeType == 1,
color: AppColors.down,
color: AppColorScheme.down,
onTap: () => onChanged(1),
),
),
@@ -386,10 +388,10 @@ class _TypeButton extends StatelessWidget {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm + AppSpacing.xs),
decoration: BoxDecoration(
color: isSelected ? color : Colors.transparent,
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(AppRadius.md),
border: isSelected ? null : Border.all(color: color),
),
child: Center(
@@ -441,7 +443,7 @@ class _TradeButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final color = isBuy ? AppColors.up : AppColors.down;
final color = isBuy ? AppColorScheme.up : AppColorScheme.down;
return SizedBox(
width: double.infinity,
@@ -453,7 +455,7 @@ class _TradeButton extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(isBuy ? LucideIcons.arrowDownToLine : LucideIcons.arrowUpFromLine, size: 18, color: Colors.white),
const SizedBox(width: 8),
SizedBox(width: AppSpacing.sm),
Text(
'${isBuy ? '买入' : '卖出'} ${coinCode ?? ''}',
style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600),