feat: 集成离线数字货币SVG图标系统

- 下载16个主流币种SVG图标(BTC/ETH/USDT/BNB/SOL/XRP/ADA/DOGE/DOT/MATIC/SHIB/LTC/AVAX/LINK/UNI/ATOM)
- 图标来源: spothq/cryptocurrency-icons (开源)
- 更新CoinIcon组件使用flutter_svg加载SVG格式
- 添加flutter_svg依赖
- 构建验证通过
This commit is contained in:
2026-04-09 00:34:17 +08:00
parent 8d3c3b36df
commit 491fcfdb5d
19 changed files with 177 additions and 98 deletions

View File

@@ -1,105 +1,101 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
/// 数字货币图标组件
///
/// 支持离线图标和兜底显示
///
/// 支持离线 SVG 图标和兜底显示
class CoinIcon extends StatelessWidget {
final String symbol; // 币种代码,如 'BTC', 'ETH'
final double size; // 图标大小,默认 40
final bool isCircle; // 是否圆形背景,默认 false
const CoinIcon({
super.key,
required this.symbol,
this.size = 40,
this.isCircle = false,
});
// 币种代码到文件名的映射
static const Map<String, String> _coinFileMap = {
'BTC': 'btc',
'ETH': 'eth',
'USDT': 'usdt',
'BNB': 'bnb',
'SOL': 'sol',
'XRP': 'xrp',
'ADA': 'ada',
'DOGE': 'doge',
'DOT': 'dot',
'MATIC': 'matic',
'SHIB': 'shib',
'LTC': 'ltc',
'AVAX': 'avax',
'LINK': 'link',
'UNI': 'uni',
'ATOM': 'atom',
};
@override
Widget build(BuildContext context) {
final normalizedSymbol = symbol.toUpperCase();
final fileName = _coinFileMap[normalizedSymbol];
Widget icon;
if (fileName != null) {
// 尝试加载本地图标
icon = Image.asset(
'assets/icons/crypto/$fileName.png',
width: size,
height: size,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
// 加载失败显示兜底UI
return _buildFallback(context, normalizedSymbol);
},
);
} else {
// 没有对应的图标显示兜底UI
icon = _buildFallback(context, normalizedSymbol);
}
if (isCircle) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surfaceContainerHigh,
),
child: Center(child: icon),
);
}
return icon;
final String symbol; // 币种代码,如 'BTC', 'ETH'
final double size; // 图标大小,默认 40
final bool isCircle; // 是否圆形背景,默认 false
const CoinIcon({
super.key,
required this.symbol,
this.size = 40,
this.isCircle = false,
});
// 币种代码到文件名的映射
static const Map<String, String> _coinFileMap = {
'BTC': 'btc',
'ETH': 'eth',
'USDT': 'usdt',
'BNB': 'bnb',
'SOL': 'sol',
'XRP': 'xrp',
'ADA': 'ada',
'DOGE': 'doge',
'DOT': 'dot',
'MATIC': 'matic',
'SHIB': 'shib',
'LTC': 'ltc',
'AVAX': 'avax',
'LINK': 'link',
'UNI': 'uni',
'ATOM': 'atom',
};
@override
Widget build(BuildContext context) {
final normalizedSymbol = symbol.toUpperCase();
final fileName = _coinFileMap[normalizedSymbol];
Widget icon;
if (fileName != null) {
icon = SvgPicture.asset(
'assets/icons/crypto/$fileName.svg',
width: size,
height: size,
fit: BoxFit.contain,
placeholderBuilder: (context) => _buildFallback(context, normalizedSymbol),
);
} else {
icon = _buildFallback(context, normalizedSymbol);
}
/// 构建兜底UI显示币种代码
Widget _buildFallback(BuildContext context, String symbol) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary,
],
),
),
child: Center(
child: Text(
symbol.length > 3 ? symbol.substring(0, 3) : symbol,
style: TextStyle(
color: Colors.white,
fontSize: size * 0.35,
fontWeight: FontWeight.bold,
),
),
),
);
if (isCircle) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surfaceContainerHigh,
),
child: Center(child: icon),
);
}
return icon;
}
/// 构建兜底UI显示币种代码
Widget _buildFallback(BuildContext context, String symbol) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary,
],
),
),
child: Center(
child: Text(
symbol.length > 3 ? symbol.substring(0, 3) : symbol,
style: TextStyle(
color: Colors.white,
fontSize: size * 0.35,
fontWeight: FontWeight.bold,
),
),
),
);
}
}