Files
monisuo/flutter_monisuo/lib/ui/pages/asset/transfer_page.dart

987 lines
32 KiB
Dart
Raw Normal View History

import 'dart:async';
2026-03-27 20:40:51 +08:00
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../../core/theme/app_theme.dart';
import '../../../core/theme/app_theme_extension.dart';
2026-03-27 20:40:51 +08:00
import '../../../core/theme/app_spacing.dart';
import '../../../providers/asset_provider.dart';
import '../../../data/models/account_models.dart';
/// 劃轉頁面 - 現代化設計版本
2026-03-27 20:40:51 +08:00
class TransferPage extends StatefulWidget {
const TransferPage({super.key});
@override
State<TransferPage> createState() => _TransferPageState();
}
class _TransferPageState extends State<TransferPage>
with TickerProviderStateMixin {
2026-03-27 20:40:51 +08:00
final _amountController = TextEditingController();
final _focusNode = FocusNode();
2026-04-07 01:05:05 +08:00
int _direction = 1; // 1: 資金→交易, 2: 交易→資金
2026-03-27 20:40:51 +08:00
bool _isLoading = false;
// Animation controllers
late AnimationController _swapAnimationController;
late AnimationController _amountAnimationController;
late AnimationController _balanceAnimationController;
late Animation<double> _swapAnimation;
late Animation<double> _amountScaleAnimation;
// Balance animation values
String _displayedFromBalance = '0.00';
String _displayedToBalance = '0.00';
String _targetFromBalance = '0.00';
String _targetToBalance = '0.00';
2026-03-27 20:40:51 +08:00
@override
void initState() {
super.initState();
// Initialize swap animation
_swapAnimationController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_swapAnimation = CurvedAnimation(
parent: _swapAnimationController,
curve: Curves.easeInOutCubic,
);
// Initialize amount scale animation
_amountAnimationController = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
_amountScaleAnimation = Tween<double>(begin: 1.0, end: 1.05).animate(
CurvedAnimation(
parent: _amountAnimationController,
curve: Curves.easeInOut,
),
);
// Initialize balance animation
_balanceAnimationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
)..addListener(_updateBalanceAnimation);
_amountController.addListener(_onAmountChanged);
2026-03-27 20:40:51 +08:00
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<AssetProvider>().refreshAll(force: true);
_updateBalanceTargets();
2026-03-27 20:40:51 +08:00
});
}
@override
void dispose() {
_amountController.removeListener(_onAmountChanged);
2026-03-27 20:40:51 +08:00
_amountController.dispose();
_focusNode.dispose();
_swapAnimationController.dispose();
_amountAnimationController.dispose();
_balanceAnimationController.removeListener(_updateBalanceAnimation);
_balanceAnimationController.dispose();
2026-03-27 20:40:51 +08:00
super.dispose();
}
void _onAmountChanged() {
// Trigger amount input animation
_amountAnimationController.forward().then((_) {
_amountAnimationController.reverse();
});
}
void _updateBalanceTargets() {
setState(() {
_targetFromBalance = _fromBalance;
_targetToBalance = _toBalance;
_displayedFromBalance = _targetFromBalance;
_displayedToBalance = _targetToBalance;
});
}
void _updateBalanceAnimation() {
// This will be used for smooth balance transitions
}
// ============================================
2026-04-07 01:05:05 +08:00
// 數據訪問
// ============================================
2026-03-27 20:40:51 +08:00
String get _fundBalance {
try {
final provider = context.read<AssetProvider>();
final balance = provider.fundAccount?.balance ?? provider.overview?.fundBalance ?? '0.00';
return _formatBalance(balance);
} catch (e) {
return '0.00';
}
2026-03-27 20:40:51 +08:00
}
String get _tradeUsdtBalance {
try {
final provider = context.read<AssetProvider>();
if (provider.tradeAccounts.isEmpty) {
return '0.00';
}
final usdtHolding = provider.tradeAccounts.firstWhere(
(t) => t.coinCode.toUpperCase() == 'USDT',
orElse: () => AccountTrade(
id: 0,
userId: 0,
coinCode: 'USDT',
quantity: '0',
avgPrice: '1',
totalCost: '0',
currentValue: '0',
profit: '0',
profitRate: 0,
),
);
return _formatBalance(usdtHolding.quantity);
} catch (e) {
return '0.00';
}
2026-03-27 20:40:51 +08:00
}
String get _availableBalance => _direction == 1 ? _fundBalance : _tradeUsdtBalance;
2026-03-27 20:40:51 +08:00
2026-04-07 01:05:05 +08:00
String get _fromLabel => _direction == 1 ? '資金賬戶' : '交易賬戶';
String get _toLabel => _direction == 1 ? '交易賬戶' : '資金賬戶';
String get _fromBalance => _direction == 1 ? _fundBalance : _tradeUsdtBalance;
String get _toBalance => _direction == 1 ? _tradeUsdtBalance : _fundBalance;
IconData get _fromIcon => _direction == 1 ? LucideIcons.wallet : LucideIcons.trendingUp;
IconData get _toIcon => _direction == 1 ? LucideIcons.trendingUp : LucideIcons.wallet;
// ============================================
2026-04-07 01:05:05 +08:00
// 業務邏輯
// ============================================
2026-03-27 20:40:51 +08:00
Future<void> _doTransfer() async {
final amount = _amountController.text;
final available = double.tryParse(_availableBalance) ?? 0;
final transferAmount = double.tryParse(amount) ?? 0;
if (transferAmount <= 0) {
2026-04-07 01:05:05 +08:00
_showSnackBar('請輸入有效的劃轉金額');
2026-03-27 20:40:51 +08:00
return;
}
if (transferAmount > available) {
2026-04-07 01:05:05 +08:00
_showSnackBar('餘額不足');
2026-03-27 20:40:51 +08:00
return;
}
setState(() => _isLoading = true);
try {
final response = await context.read<AssetProvider>().transfer(
direction: _direction,
amount: amount,
);
if (mounted) {
if (response.success) {
_amountController.clear();
2026-04-07 01:05:05 +08:00
_showSnackBar('劃轉成功');
await Future.delayed(const Duration(milliseconds: 500));
if (mounted) {
Navigator.of(context).pop(true);
}
2026-03-27 20:40:51 +08:00
} else {
2026-04-07 01:05:05 +08:00
_showSnackBar(response.message ?? '劃轉失敗');
2026-03-27 20:40:51 +08:00
}
}
} finally {
if (mounted) {
setState(() => _isLoading = false);
}
}
}
void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
2026-03-27 20:40:51 +08:00
);
}
void _setQuickAmount(double percent) {
final available = double.tryParse(_availableBalance) ?? 0;
final amount = available * percent;
_amountController.text = amount.toStringAsFixed(8).replaceAll(RegExp(r'\.?0+$'), '');
// Trigger haptic feedback
HapticFeedback.selectionClick();
}
Future<void> _toggleDirection() async {
// Trigger haptic feedback
HapticFeedback.mediumImpact();
await _swapAnimationController.forward();
setState(() {
_direction = _direction == 1 ? 2 : 1;
_updateBalanceTargets();
});
_swapAnimationController.reset();
}
// ============================================
2026-04-07 01:05:05 +08:00
// 構建 UI
// ============================================
2026-03-27 20:40:51 +08:00
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
2026-03-27 20:40:51 +08:00
return Scaffold(
backgroundColor: colorScheme.surface,
2026-03-27 20:40:51 +08:00
appBar: AppBar(
backgroundColor: Colors.transparent,
2026-03-27 20:40:51 +08:00
elevation: 0,
scrolledUnderElevation: 0,
2026-03-27 20:40:51 +08:00
leading: IconButton(
icon: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: Icon(LucideIcons.arrowLeft, color: colorScheme.onSurface, size: 20),
),
2026-03-27 20:40:51 +08:00
onPressed: () => Navigator.of(context).pop(),
),
title: Text(
2026-04-07 01:05:05 +08:00
'賬戶劃轉',
style: AppTextStyles.headlineLarge(context).copyWith(
fontWeight: FontWeight.w700,
),
2026-03-27 20:40:51 +08:00
),
centerTitle: true,
),
body: Consumer<AssetProvider>(
builder: (context, provider, _) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
colorScheme.surface,
colorScheme.surfaceContainerLowest.withValues(alpha: 0.3),
],
),
),
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(AppSpacing.lg, AppSpacing.sm, AppSpacing.lg, AppSpacing.xl),
child: Column(
children: [
_buildTransferDirectionCard(),
const SizedBox(height: AppSpacing.xl),
_buildAmountSection(),
const SizedBox(height: AppSpacing.xl),
_buildTipsCard(),
const SizedBox(height: AppSpacing.xl + AppSpacing.sm),
_buildConfirmButton(),
const SizedBox(height: AppSpacing.lg),
],
),
2026-03-27 20:40:51 +08:00
),
);
},
),
);
}
// ============================================
// Transfer direction card - Modern Design
// ============================================
Widget _buildTransferDirectionCard() {
final colorScheme = Theme.of(context).colorScheme;
final isDark = colorScheme.brightness == Brightness.dark;
// Gold gradient colors
final goldStart = isDark ? const Color(0xFFD4AF37) : const Color(0xFFF59E0B);
final goldEnd = isDark ? const Color(0xFFFFD700) : const Color(0xFFFFA500);
2026-03-27 20:40:51 +08:00
return Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.lg),
2026-03-27 20:40:51 +08:00
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDark
? [
colorScheme.surfaceContainerHigh,
colorScheme.surfaceContainer,
]
: [
Colors.white,
colorScheme.surfaceContainerLow,
],
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: colorScheme.shadow.withValues(alpha: isDark ? 0.3 : 0.08),
blurRadius: 24,
offset: const Offset(0, 8),
),
BoxShadow(
color: goldStart.withValues(alpha: 0.1),
blurRadius: 16,
offset: const Offset(0, 4),
spreadRadius: 2,
),
],
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.3),
width: 1,
),
2026-03-27 20:40:51 +08:00
),
child: Column(
2026-03-27 20:40:51 +08:00
children: [
// From Account Card
_AnimatedAccountCard(
key: ValueKey('from-$_direction'),
label: '',
accountName: _fromLabel,
balance: _displayedFromBalance,
icon: _fromIcon,
isSource: true,
animation: _swapAnimation,
2026-03-27 20:40:51 +08:00
),
// Swap Button - Enhanced Design
Padding(
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
child: GestureDetector(
onTap: _toggleDirection,
child: Container(
width: 56,
height: 56,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [goldStart, goldEnd],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: goldStart.withValues(alpha: 0.4),
blurRadius: 16,
offset: const Offset(0, 4),
),
BoxShadow(
color: goldStart.withValues(alpha: 0.2),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Container(
margin: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: colorScheme.surface,
shape: BoxShape.circle,
),
child: Container(
margin: const EdgeInsets.all(2),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [goldStart, goldEnd],
),
shape: BoxShape.circle,
),
child: Icon(
LucideIcons.arrowUpDown,
size: 24,
color: colorScheme.surface,
),
),
),
),
),
),
// To Account Card
_AnimatedAccountCard(
key: ValueKey('to-$_direction'),
label: '',
accountName: _toLabel,
balance: _displayedToBalance,
icon: _toIcon,
isSource: false,
animation: _swapAnimation,
),
],
),
);
}
// ============================================
// Amount input section - Enhanced Design
// ============================================
Widget _buildAmountSection() {
final colorScheme = Theme.of(context).colorScheme;
final isDark = colorScheme.brightness == Brightness.dark;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header with label and max button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(
LucideIcons.coins,
size: 20,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(width: 8),
Text(
'劃轉金額',
style: AppTextStyles.headlineSmall(context).copyWith(
color: colorScheme.onSurface,
fontWeight: FontWeight.w600,
),
),
],
),
Material(
color: Colors.transparent,
child: InkWell(
onTap: () => _setQuickAmount(1.0),
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
colorScheme.secondary.withValues(alpha: 0.15),
colorScheme.secondary.withValues(alpha: 0.05),
],
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: colorScheme.secondary.withValues(alpha: 0.3),
width: 1,
),
),
child: Text(
'全部',
style: AppTextStyles.labelLarge(context).copyWith(
color: colorScheme.secondary,
fontWeight: FontWeight.w600,
),
),
),
),
),
],
),
const SizedBox(height: 16),
// Amount input card with glow effect
AnimatedBuilder(
animation: _amountScaleAnimation,
builder: (context, child) {
return Transform.scale(
scale: _amountScaleAnimation.value,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
boxShadow: _focusNode.hasFocus
? [
BoxShadow(
color: colorScheme.secondary.withValues(alpha: 0.2),
blurRadius: 16,
spreadRadius: 2,
),
]
: [],
),
child: Container(
width: double.infinity,
height: 80,
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg, vertical: AppSpacing.md),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDark
? [
colorScheme.surfaceContainerHighest,
colorScheme.surfaceContainerHigh,
]
: [
Colors.white,
colorScheme.surfaceContainerLow,
],
),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: _focusNode.hasFocus
? colorScheme.secondary
: colorScheme.outlineVariant.withValues(alpha: 0.5),
width: _focusNode.hasFocus ? 2 : 1,
),
boxShadow: [
BoxShadow(
color: colorScheme.shadow.withValues(alpha: isDark ? 0.2 : 0.05),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Expanded(
child: TextField(
controller: _amountController,
focusNode: _focusNode,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,8}')),
],
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.w700,
color: colorScheme.onSurface,
letterSpacing: -0.5,
),
decoration: InputDecoration(
hintText: '0.00',
hintStyle: TextStyle(
fontSize: 36,
fontWeight: FontWeight.w700,
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.3),
letterSpacing: -0.5,
),
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
isDense: true,
),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: Text(
'USDT',
style: AppTextStyles.headlineSmall(context).copyWith(
fontWeight: FontWeight.w600,
color: colorScheme.onSurfaceVariant,
),
),
),
],
2026-03-27 20:40:51 +08:00
),
),
),
);
},
),
const SizedBox(height: 16),
// Available balance display
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'可用餘額',
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
Text(
'${_formatBalance(_availableBalance)} USDT',
style: AppTextStyles.bodyLarge(context).copyWith(
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
],
),
),
const SizedBox(height: 20),
// Percent buttons - Capsule design with gradient
Row(
children: [
_buildPercentButton('25%', 0.25, 0),
const SizedBox(width: 8),
_buildPercentButton('50%', 0.50, 1),
const SizedBox(width: 8),
_buildPercentButton('75%', 0.75, 2),
const SizedBox(width: 8),
_buildPercentButton('100%', 1.0, 3),
],
),
],
2026-03-27 20:40:51 +08:00
);
}
Widget _buildPercentButton(String label, double percent, int index) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = colorScheme.brightness == Brightness.dark;
final goldStart = isDark ? const Color(0xFFD4AF37) : const Color(0xFFF59E0B);
final goldEnd = isDark ? const Color(0xFFFFD700) : const Color(0xFFFFA500);
return Expanded(
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => _setQuickAmount(percent),
borderRadius: BorderRadius.circular(20),
child: Container(
height: 44,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
goldStart.withValues(alpha: 0.12),
goldEnd.withValues(alpha: 0.08),
],
),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: goldStart.withValues(alpha: 0.25),
width: 1.5,
),
),
child: Center(
child: Text(
label,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: goldStart,
),
),
),
),
2026-03-27 20:40:51 +08:00
),
),
);
}
// ============================================
// Tips card - Refined Design
// ============================================
Widget _buildTipsCard() {
final colorScheme = Theme.of(context).colorScheme;
final isDark = colorScheme.brightness == Brightness.dark;
final successColor = isDark ? const Color(0xFF4ADE80) : const Color(0xFF10B981);
2026-03-27 20:40:51 +08:00
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
2026-03-27 20:40:51 +08:00
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
successColor.withValues(alpha: 0.08),
successColor.withValues(alpha: 0.03),
],
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: successColor.withValues(alpha: 0.2),
width: 1,
),
2026-03-27 20:40:51 +08:00
),
child: Row(
2026-03-27 20:40:51 +08:00
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: successColor.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
LucideIcons.sparkles,
size: 18,
color: successColor,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'即時到賬',
style: AppTextStyles.bodyMedium(context).copyWith(
color: successColor,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 2),
Text(
'劃轉即時到賬,無需手續費',
style: AppTextStyles.bodySmall(context).copyWith(
color: successColor.withValues(alpha: 0.8),
),
),
],
2026-03-27 20:40:51 +08:00
),
),
],
),
);
}
// ============================================
// Confirm button - Premium Design
// ============================================
Widget _buildConfirmButton() {
final colorScheme = Theme.of(context).colorScheme;
final isDark = colorScheme.brightness == Brightness.dark;
final goldStart = isDark ? const Color(0xFFD4AF37) : const Color(0xFFF59E0B);
final goldEnd = isDark ? const Color(0xFFFFD700) : const Color(0xFFFFA500);
final hasAmount = _amountController.text.isNotEmpty && double.tryParse(_amountController.text) != null && double.parse(_amountController.text) > 0;
return Container(
height: 60,
decoration: BoxDecoration(
gradient: hasAmount && !_isLoading
? LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [goldStart, goldEnd],
)
: null,
color: hasAmount && !_isLoading ? null : colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(20),
boxShadow: hasAmount && !_isLoading
? [
BoxShadow(
color: goldStart.withValues(alpha: 0.4),
blurRadius: 20,
offset: const Offset(0, 8),
),
BoxShadow(
color: goldStart.withValues(alpha: 0.2),
blurRadius: 8,
offset: const Offset(0, 4),
),
]
: [
BoxShadow(
color: colorScheme.shadow.withValues(alpha: 0.08),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: _isLoading ? null : _doTransfer,
borderRadius: BorderRadius.circular(20),
child: Center(
child: _isLoading
? SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2.5,
valueColor: AlwaysStoppedAnimation<Color>(
isDark ? Colors.white : colorScheme.onSurface,
),
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
LucideIcons.arrowRight,
size: 22,
color: hasAmount ? Colors.white : colorScheme.onSurfaceVariant,
),
const SizedBox(width: 8),
Text(
'確認劃轉',
style: TextStyle(
color: hasAmount ? Colors.white : colorScheme.onSurfaceVariant,
fontSize: 18,
fontWeight: FontWeight.w700,
letterSpacing: 0.5,
),
),
],
),
2026-03-27 20:40:51 +08:00
),
),
2026-03-27 20:40:51 +08:00
),
);
}
// ============================================
// Helpers
// ============================================
String _formatBalance(String balance) {
final val = double.tryParse(balance);
if (val == null) return '0.00';
return val.toStringAsFixed(2);
}
}
// ============================================
// Animated Account Card Widget
// ============================================
class _AnimatedAccountCard extends StatelessWidget {
final String label;
final String accountName;
final String balance;
final IconData icon;
final bool isSource;
final Animation<double> animation;
const _AnimatedAccountCard({
super.key,
required this.label,
required this.accountName,
required this.balance,
required this.icon,
required this.isSource,
required this.animation,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = colorScheme.brightness == Brightness.dark;
final goldColor = isDark ? const Color(0xFFD4AF37) : const Color(0xFFF59E0B);
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Transform.translate(
offset: Offset(0, animation.value * (isSource ? -10 : 10)),
child: Opacity(
opacity: 1.0 - (animation.value * 0.3),
child: child,
),
);
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: isDark
? colorScheme.surfaceContainer.withValues(alpha: 0.5)
: colorScheme.surface.withValues(alpha: 0.8),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.2),
width: 1,
),
),
child: Row(
children: [
// Icon container with gradient
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
goldColor.withValues(alpha: 0.15),
goldColor.withValues(alpha: 0.05),
],
),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: goldColor.withValues(alpha: 0.2),
width: 1,
),
),
child: Icon(
icon,
size: 22,
color: goldColor,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Text(
accountName,
style: AppTextStyles.headlineSmall(context).copyWith(
fontWeight: FontWeight.w600,
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'¥ $balance',
style: AppTextStyles.headlineSmall(context).copyWith(
fontWeight: FontWeight.w700,
color: colorScheme.onSurface,
),
),
const SizedBox(height: 4),
Text(
'USDT',
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
],
),
),
);
}
}