111
This commit is contained in:
@@ -1,73 +1,130 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../../data/models/coin.dart';
|
||||
import '../../../components/coin_icon.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
|
||||
/// 價格卡片組件
|
||||
///
|
||||
/// 顯示當前幣種價格和 24h 漲跌幅。
|
||||
/// 佈局:大號價格(32px bold) + 漲跌幅徽章(圓角sm,漲綠背景) + "24h 變化" 副標題。
|
||||
/// 交易页顶部信息区:代币信息(左) + 价格信息(右) 同行展示
|
||||
class PriceCard extends StatelessWidget {
|
||||
final Coin coin;
|
||||
const PriceCard({super.key, required this.coin});
|
||||
final String tradingStatus;
|
||||
final VoidCallback? onTapCoin;
|
||||
|
||||
const PriceCard({
|
||||
super.key,
|
||||
required this.coin,
|
||||
this.tradingStatus = 'trading',
|
||||
this.onTapCoin,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final isUp = coin.isUp;
|
||||
final changeColor =
|
||||
isUp ? context.appColors.up : context.appColors.down;
|
||||
final changeBgColor = isUp
|
||||
? context.appColors.upBackground
|
||||
: context.appColors.downBackground;
|
||||
final changeColor = isUp ? colorScheme.tertiary : colorScheme.error;
|
||||
final isTrading = tradingStatus == 'trading';
|
||||
final isLunch = tradingStatus == 'lunch_break';
|
||||
final statusLabel = isTrading ? '交易中' : (isLunch ? '午休中' : '已收盘');
|
||||
final statusColor = isTrading ? colorScheme.tertiary : (isLunch ? Colors.orange : colorScheme.onSurfaceVariant);
|
||||
final showPrice = true;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(20), // 24px → 20px
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.surfaceCard,
|
||||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||||
border: Border.all(
|
||||
color: context.appColors.ghostBorder,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: AppSpacing.sm),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// 價格行:大號價格 + 漲跌幅徽章
|
||||
Row(
|
||||
// 左侧:代币信息
|
||||
GestureDetector(
|
||||
onTap: onTapCoin,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CoinIcon(symbol: coin.code, size: 28),
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
coin.code,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: colorScheme.onSurface),
|
||||
),
|
||||
Text(
|
||||
' /USDT',
|
||||
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Icon(LucideIcons.chevronDown, size: 12, color: colorScheme.onSurfaceVariant),
|
||||
],
|
||||
),
|
||||
// 交易状态
|
||||
Text(
|
||||
statusLabel,
|
||||
style: TextStyle(fontSize: 11, color: statusColor, fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// 右侧:价格 + 涨跌幅
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
coin.formattedPrice,
|
||||
style: AppTextStyles.numberLarge(context).copyWith(fontSize: 32),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
// 漲跌幅徽章 - 圓角sm,漲綠背景
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 4), // 調整 padding
|
||||
decoration: BoxDecoration(
|
||||
color: changeBgColor,
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
),
|
||||
child: Text(
|
||||
coin.formattedChange,
|
||||
style: AppTextStyles.numberSmall(context).copyWith(
|
||||
color: changeColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text(
|
||||
showPrice ? coin.formattedPrice : '--',
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: showPrice ? colorScheme.onSurface : colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showPrice) ...[
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
coin.formattedChange,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: changeColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
// 24h 统计 — 小字一行
|
||||
Text(
|
||||
'高 ${_fmt(coin.high24h)} 低 ${_fmt(coin.low24h)} 量 ${_fmtVol(coin.volume24h)}',
|
||||
style: TextStyle(fontSize: 10, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
// 副標題
|
||||
Text(
|
||||
'24h 變化',
|
||||
style: AppTextStyles.bodySmall(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _fmt(double? v) {
|
||||
if (v == null) return '--';
|
||||
if (v >= 1000) return v.toStringAsFixed(2);
|
||||
if (v >= 1) return v.toStringAsFixed(4);
|
||||
return v.toStringAsFixed(6);
|
||||
}
|
||||
|
||||
String _fmtVol(double? v) {
|
||||
if (v == null) return '--';
|
||||
if (v >= 1000000) return '${(v / 1000000).toStringAsFixed(2)}M';
|
||||
if (v >= 1000) return '${(v / 1000).toStringAsFixed(2)}K';
|
||||
return v.toStringAsFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user