coin_icon.dart 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import 'package:cached_network_image/cached_network_image.dart';
  2. import 'package:flutter/material.dart';
  3. /// 币种图标组件:图标已在 app 启动时通过 coinCacheProvider 预缓存,
  4. /// 切换到行情页时命中缓存直接显示。加载失败时显示字母占位。
  5. class CoinIcon extends StatelessWidget {
  6. const CoinIcon({
  7. super.key,
  8. required this.symbol,
  9. this.iconUrl = '',
  10. this.size = 40,
  11. this.borderRadius,
  12. this.shape = BoxShape.rectangle,
  13. });
  14. final String symbol;
  15. final String iconUrl;
  16. final double size;
  17. /// 圆角半径,仅在 [shape] 为 [BoxShape.rectangle] 时生效
  18. final double? borderRadius;
  19. /// [BoxShape.circle] 或 [BoxShape.rectangle](默认矩形+圆角)
  20. final BoxShape shape;
  21. @override
  22. Widget build(BuildContext context) {
  23. final letter = symbol.isNotEmpty ? symbol[0].toUpperCase() : '?';
  24. final cs = Theme.of(context).colorScheme;
  25. final fontSize = size * 0.42;
  26. Widget letterWidget = SizedBox(
  27. width: size,
  28. height: size,
  29. child: Center(
  30. child: Text(
  31. letter,
  32. style: TextStyle(
  33. color: cs.onSurface.withAlpha(153),
  34. fontSize: fontSize,
  35. fontWeight: FontWeight.w700,
  36. ),
  37. ),
  38. ),
  39. );
  40. if (iconUrl.isEmpty) return _clip(letterWidget);
  41. // Key 随 URL 变化,避免从行情等错误来源切回空 icon 时仍短暂显示旧图
  42. return _clip(
  43. CachedNetworkImage(
  44. key: ValueKey('$symbol|$iconUrl'),
  45. imageUrl: iconUrl,
  46. width: size,
  47. height: size,
  48. fit: BoxFit.cover,
  49. errorWidget: (_, __, ___) => letterWidget,
  50. ),
  51. );
  52. }
  53. Widget _clip(Widget child) {
  54. if (shape == BoxShape.circle) {
  55. return ClipOval(child: child);
  56. }
  57. final radius = borderRadius ?? size * 0.3;
  58. return ClipRRect(
  59. borderRadius: BorderRadius.circular(radius),
  60. child: child,
  61. );
  62. }
  63. }