// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/animated_nav_label.dart'; import '../../../l10n/app_localizations.dart'; import '../recommend_types.dart'; import 'search_gift_glyphs.dart'; const double _navHeight = 56; const double _tabGap = 20; const double _tabFont = 18; const double _maxNavigationTextScale = 1.3; const double _searchIconSize = 26; const double _giftIconSize = 26; const double _underlineWidth = 26; const double _underlineHeight = 3; class RecommendTopNav extends StatelessWidget { const RecommendTopNav({ super.key, required this.selected, required this.onSelected, required this.onSearch, this.onVideo = false, this.onGift, this.pagePosition, }); static const double height = _navHeight; final RecommendInnerTab selected; final ValueChanged onSelected; final VoidCallback onSearch; final bool onVideo; /// 首页横向页面的连续位置,用于文字和下划线跟手过渡。 final double? pagePosition; /// 右上角礼包入口;为空时不显示。 final VoidCallback? onGift; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final items = [ (RecommendInnerTab.feed, l10n.recommendTopTab, 'recommend'), (RecommendInnerTab.playlists, l10n.playlistTopTab, 'playlist'), (RecommendInnerTab.allDramas, l10n.allDramasTopTab, 'all'), ]; final shadows = onVideo ? const [Shadow(color: AppColors.mediaBlack, blurRadius: 6)] : null; return MediaQuery.withClampedTextScaling( maxScaleFactor: _maxNavigationTextScale, child: SizedBox( key: const Key('recommend-top-nav'), height: height, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.only(left: 20, right: 8), child: Row( mainAxisSize: MainAxisSize.min, children: [ for (var index = 0; index < items.length; index++) ...[ if (index > 0) const SizedBox(width: _tabGap), _tab(items[index], shadows, context), ], ], ), ), ), SizedBox.square( dimension: AppDimens.tapTarget, child: IconButton( key: const Key('recommend-search'), tooltip: l10n.search, onPressed: onSearch, padding: EdgeInsets.zero, constraints: const BoxConstraints.tightFor( width: AppDimens.tapTarget, height: AppDimens.tapTarget, ), iconSize: _searchIconSize, color: AppColors.textPrimary, icon: SearchGlyph( size: _searchIconSize, color: AppColors.textPrimary, ), ), ), if (onGift != null) SizedBox.square( dimension: AppDimens.tapTarget, child: IconButton( key: const Key('recommend-gift'), tooltip: l10n.wallet, onPressed: onGift, padding: EdgeInsets.zero, constraints: const BoxConstraints.tightFor( width: AppDimens.tapTarget, height: AppDimens.tapTarget, ), iconSize: _giftIconSize, icon: GiftGlyph(size: _giftIconSize), ), ), const SizedBox(width: 8), ], ), ), ); } Widget _tab( (RecommendInnerTab, String, String) item, List? shadows, BuildContext context, ) { final (tab, label, code) = item; final isSelected = selected == tab; final position = pagePosition; final progress = position == null ? null : (1 - (position - tab.index).abs()).clamp(0.0, 1.0); return GestureDetector( key: Key('recommend-tab-$code'), behavior: HitTestBehavior.opaque, onTap: () => onSelected(tab), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 补偿文字下方的 4pt 间距 + 3pt 下划线,让文字视觉中心与右侧图标对齐。 const SizedBox(height: 4 + _underlineHeight), AnimatedNavLabel( label: label, selected: isSelected, selectionProgress: progress, inactiveColor: AppColors.homeTabInactive, style: TextStyle( color: AppColors.textPrimary, fontSize: _tabFont, fontWeight: FontWeight.w400, letterSpacing: 0, height: 1.0, shadows: shadows, ), ), const SizedBox(height: 4), _underline( code, progress, isSelected, MediaQuery.disableAnimationsOf(context), ), ], ), ); } Widget _underline( String code, double? progress, bool isSelected, bool reduceMotion, ) { if (progress != null) return _line(code, progress); final target = isSelected ? 1.0 : 0.0; return TweenAnimationBuilder( tween: Tween(begin: target, end: target), duration: reduceMotion ? Duration.zero : AppDimens.homePrimaryTabAnimationDuration, curve: Curves.easeOut, builder: (_, value, _) => _line(code, value), ); } Widget _line(String code, double progress) => Container( key: Key('recommend-tab-$code-underline'), width: _underlineWidth, height: _underlineHeight, decoration: BoxDecoration( color: progress == 0 ? AppColors.transparent : AppColors.brandRed.withValues(alpha: progress), borderRadius: BorderRadius.circular(_underlineHeight / 2), ), ); }