ido_overview_cards.dart 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:go_router/go_router.dart';
  4. import 'package:intl/intl.dart';
  5. import '../../../core/l10n/app_localizations.dart';
  6. import '../../../core/theme/app_colors.dart';
  7. import '../../../providers/asset_provider.dart';
  8. import '../../../providers/futures_provider.dart' show activeBottomTabProvider;
  9. /// 资产页锁仓账户子 Tab 索引
  10. const int kAssetStakingSubTabIndex = 5;
  11. /// IDO 预售页「我的团队 / 锁仓总量」入口卡片(对齐 Web ido-overview)
  12. class IdoOverviewCards extends ConsumerWidget {
  13. const IdoOverviewCards({
  14. super.key,
  15. required this.teamNum,
  16. required this.lockedTotal,
  17. this.loading = false,
  18. });
  19. final int teamNum;
  20. final String lockedTotal;
  21. final bool loading;
  22. static String _fmtAmount(String value, {int maxFrac = 8}) {
  23. final parsed = double.tryParse(value);
  24. if (parsed == null || !parsed.isFinite) {
  25. return '0';
  26. }
  27. if (parsed == 0) {
  28. return '0';
  29. }
  30. final pattern = maxFrac > 0 ? '#,##0.${'#' * maxFrac}' : '#,##0';
  31. return NumberFormat(pattern, 'en_US').format(parsed);
  32. }
  33. void _goToStakingAccount(BuildContext context, WidgetRef ref) {
  34. ref.read(currentAssetSubTabProvider.notifier).state =
  35. kAssetStakingSubTabIndex;
  36. ref.read(activeBottomTabProvider.notifier).state = kAssetStakingSubTabIndex;
  37. context.go('/asset?tab=staking');
  38. }
  39. @override
  40. Widget build(BuildContext context, WidgetRef ref) {
  41. final l10n = AppLocalizations.of(context)!;
  42. final isDark = Theme.of(context).brightness == Brightness.dark;
  43. final primary =
  44. isDark ? Colors.white : Theme.of(context).colorScheme.onSurface;
  45. final labelColor = primary.withAlpha(140);
  46. final cardBg = primary.withAlpha(10);
  47. final cardBorder = primary.withAlpha(26);
  48. return Padding(
  49. padding: const EdgeInsets.fromLTRB(16, 0, 16, 24),
  50. child: IntrinsicHeight(
  51. child: Row(
  52. crossAxisAlignment: CrossAxisAlignment.stretch,
  53. children: [
  54. Expanded(
  55. child: _OverviewCard(
  56. label: l10n.financeMyTeam,
  57. value: loading ? '—' : _fmtAmount('$teamNum', maxFrac: 0),
  58. labelColor: labelColor,
  59. valueColor: primary,
  60. backgroundColor: cardBg,
  61. borderColor: cardBorder,
  62. onTap: () => context.push('/finance/team'),
  63. ),
  64. ),
  65. const SizedBox(width: 12),
  66. Expanded(
  67. child: _OverviewCard(
  68. label: l10n.assetsStakingTotal,
  69. value: loading ? '—' : _fmtAmount(lockedTotal, maxFrac: 2),
  70. labelColor: labelColor,
  71. valueColor: primary,
  72. backgroundColor: cardBg,
  73. borderColor: cardBorder,
  74. onTap: () => _goToStakingAccount(context, ref),
  75. ),
  76. ),
  77. ],
  78. ),
  79. ),
  80. );
  81. }
  82. }
  83. class _OverviewCard extends StatelessWidget {
  84. const _OverviewCard({
  85. required this.label,
  86. required this.value,
  87. required this.labelColor,
  88. required this.valueColor,
  89. required this.backgroundColor,
  90. required this.borderColor,
  91. required this.onTap,
  92. });
  93. final String label;
  94. final String value;
  95. final Color labelColor;
  96. final Color valueColor;
  97. final Color backgroundColor;
  98. final Color borderColor;
  99. final VoidCallback onTap;
  100. @override
  101. Widget build(BuildContext context) {
  102. return Material(
  103. color: backgroundColor,
  104. shape: RoundedRectangleBorder(
  105. borderRadius: BorderRadius.circular(16),
  106. side: BorderSide(color: borderColor),
  107. ),
  108. child: InkWell(
  109. onTap: onTap,
  110. borderRadius: BorderRadius.circular(16),
  111. splashColor: AppColors.brand.withAlpha(40),
  112. highlightColor: AppColors.brand.withAlpha(20),
  113. child: Padding(
  114. padding: const EdgeInsets.fromLTRB(20, 20, 20, 22),
  115. child: Column(
  116. crossAxisAlignment: CrossAxisAlignment.start,
  117. children: [
  118. Row(
  119. children: [
  120. Flexible(
  121. child: Text(
  122. label,
  123. style: TextStyle(color: labelColor, fontSize: 14),
  124. maxLines: 1,
  125. overflow: TextOverflow.ellipsis,
  126. ),
  127. ),
  128. Text(
  129. ' >',
  130. style: TextStyle(
  131. color: labelColor.withAlpha(180),
  132. fontSize: 13,
  133. ),
  134. ),
  135. ],
  136. ),
  137. const SizedBox(height: 12),
  138. SizedBox(
  139. height: 26,
  140. width: double.infinity,
  141. child: Align(
  142. alignment: Alignment.centerLeft,
  143. child: FittedBox(
  144. fit: BoxFit.scaleDown,
  145. alignment: Alignment.centerLeft,
  146. child: Text(
  147. value,
  148. maxLines: 1,
  149. style: TextStyle(
  150. color: valueColor,
  151. fontSize: 22,
  152. fontWeight: FontWeight.w600,
  153. height: 1.1,
  154. fontFeatures: const [FontFeature.tabularFigures()],
  155. ),
  156. ),
  157. ),
  158. ),
  159. ),
  160. ],
  161. ),
  162. ),
  163. ),
  164. );
  165. }
  166. }