// Author: fruit.com.cn // 展示可点击的金币、观看记录与收藏三列个人数据。 import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import '../../../core/data/services/auth_controller.dart'; import '../../../core/data/services/favorite_controller.dart'; import '../../../core/data/services/wallet_controller.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../l10n/app_localizations.dart'; import 'mine_stat_icon.dart'; const int _labelMaxLines = 2; const double _dividerTop = 36; /// 三项个人数据入口的现有页面回调。 class MineStatsActions { /// 绑定钱包、观看记录与收藏页面。 const MineStatsActions({ required this.onWallet, required this.onHistory, required this.onFavorites, }); final VoidCallback onWallet; final VoidCallback onHistory; final VoidCallback onFavorites; } /// 将共享的账号、钱包和收藏状态绑定到个人数据区。 class MineStatsSection extends StatelessWidget { /// 观看记录由我的页逻辑提供,其余状态沿用全局 Provider。 const MineStatsSection({ super.key, required this.watchRecordCount, required this.actions, }); final int? watchRecordCount; final MineStatsActions actions; @override Widget build(BuildContext context) { final signedIn = context.watch().isSignedIn; final wallet = context.watch(); final favorites = context.watch(); return MineStatsPanel( coinBalance: signedIn && wallet.error == null ? wallet.snapshot?.wallet.coinBalance : null, watchRecordCount: watchRecordCount, favoriteCount: favorites.loaded && favorites.error == null ? favorites.total : null, actions: actions, ); } } /// 头像与会员卡之间的个人数据,未知值与真实零值明确区分。 class MineStatsPanel extends StatelessWidget { /// 使用真实服务端总数与共享钱包余额构建数据区。 const MineStatsPanel({ super.key, required this.coinBalance, required this.watchRecordCount, required this.favoriteCount, required this.actions, }); final int? coinBalance; final int? watchRecordCount; final int? favoriteCount; final MineStatsActions actions; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Row( key: const Key('mine-stats'), crossAxisAlignment: CrossAxisAlignment.start, children: [ _tile( type: MineStatIconType.coins, value: coinBalance, label: l10n.coinBalance, onTap: actions.onWallet, ), _divider(), _tile( type: MineStatIconType.history, value: watchRecordCount, label: l10n.watchRecordsCountLabel, onTap: actions.onHistory, ), _divider(), _tile( type: MineStatIconType.favorites, value: favoriteCount, label: l10n.myFavorites, onTap: actions.onFavorites, ), ], ); } Widget _tile({ required MineStatIconType type, required int? value, required String label, required VoidCallback onTap, }) { return Expanded( child: _MineStatTile( type: type, value: value, label: label, onTap: onTap, ), ); } Widget _divider() { return Padding( padding: const EdgeInsets.only(top: _dividerTop), child: Container( width: AppDimens.mineStatsDividerWidth, height: AppDimens.mineStatsDividerHeight, color: AppColors.contentBorder, ), ); } } class _MineStatTile extends StatelessWidget { const _MineStatTile({ required this.type, required this.value, required this.label, required this.onTap, }); final MineStatIconType type; final int? value; final String label; final VoidCallback onTap; String get _key => switch (type) { MineStatIconType.coins => 'mine-stat-coins', MineStatIconType.history => 'mine-stat-watch-records', MineStatIconType.favorites => 'mine-stat-favorites', }; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final formatted = value == null ? l10n.dataUnavailable : NumberFormat.decimalPattern(l10n.localeName).format(value); return Semantics( button: true, label: label, value: formatted, onTap: onTap, excludeSemantics: true, child: Material( color: AppColors.transparent, child: InkWell( key: ValueKey(_key), onTap: onTap, excludeFromSemantics: true, borderRadius: BorderRadius.circular(AppDimens.innerRadius), child: ConstrainedBox( constraints: const BoxConstraints( minHeight: AppDimens.mineStatsMinHeight, ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: AppDimens.spaceXs, vertical: AppDimens.spaceSm, ), child: _content(formatted), ), ), ), ), ); } Widget _content(String formatted) { return Column( mainAxisSize: MainAxisSize.min, children: [ MineStatIcon(key: ValueKey('$_key-icon'), type: type), const SizedBox(height: AppDimens.spaceXs), FittedBox( fit: BoxFit.scaleDown, child: Text( formatted, key: ValueKey('$_key-value'), maxLines: 1, style: const TextStyle( fontFamily: 'Outfit', color: AppColors.textPrimary, fontSize: AppDimens.mineStatsValueFont, fontWeight: FontWeight.w600, fontFeatures: [FontFeature.tabularFigures()], height: AppDimens.mineStatsValueLineHeight, ), ), ), const SizedBox(height: AppDimens.spaceXs), Text( label, maxLines: _labelMaxLines, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: const TextStyle( color: AppColors.textTertiary, fontSize: AppDimens.mineStatsLabelFont, height: AppDimens.mineStatsLabelLineHeight, ), ), ], ); } }