// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../l10n/app_localizations.dart'; const double _topIconSize = 36; const double _topIconGlyph = 22; const double _tabGap = 22; const double _tabFontSize = 15; const double _underlineWidth = 18; const double _underlineHeight = 2; /// 首页 feed 固定顶栏:刷新 / 漫剧·真人剧·推荐 / 搜索。 class HomeFeedHeader extends StatefulWidget { const HomeFeedHeader({ super.key, required this.l10n, required this.onRefresh, required this.onSearch, required this.onMore, }); final AppLocalizations l10n; final Future Function() onRefresh; final VoidCallback onSearch; final VoidCallback onMore; @override State createState() => _HomeFeedHeaderState(); } class _HomeFeedHeaderState extends State { _FeedTab _tab = _FeedTab.recommend; bool _refreshing = false; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(8, 6, 8, 4), child: Row( children: [ _RefreshButton( tooltip: widget.l10n.retry, dimension: _topIconSize, glyphSize: _topIconGlyph, refreshing: _refreshing, onTap: _refresh, onLongPress: widget.onMore, ), Expanded( child: Center( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ for (var index = 0; index < _FeedTab.values.length; index++) _tabSlot(_FeedTab.values[index], index), ], ), ), ), ), _TopIconButton( key: const Key('home-feed-search'), tooltip: widget.l10n.search, icon: Icons.search_rounded, dimension: _topIconSize, glyphSize: _topIconGlyph, onPressed: widget.onSearch, ), ], ), ); } Widget _tabSlot(_FeedTab tab, int index) { final item = _FeedTabItem( tab: tab, label: tab.label(widget.l10n), selected: _tab == tab, fontSize: _tabFontSize, underlineWidth: _underlineWidth, underlineHeight: _underlineHeight, onTap: () => _selectTab(tab), ); if (index == 0) return item; return Padding( padding: const EdgeInsets.only(left: _tabGap), child: item, ); } void _selectTab(_FeedTab tab) { if (tab.comingSoon) { _showComingSoon(); return; } setState(() => _tab = tab); } Future _refresh() async { if (_refreshing) return; setState(() => _refreshing = true); await widget.onRefresh(); if (mounted) setState(() => _refreshing = false); } void _showComingSoon() { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(widget.l10n.comingSoon))); } } /// 顶部频道 tab:漫剧点击提示即将上线。 enum _FeedTab { anime, real, recommend; bool get comingSoon => this == _FeedTab.anime; String get keyName => switch (this) { _FeedTab.anime => 'anime', _FeedTab.real => 'real', _FeedTab.recommend => 'recommend', }; String label(AppLocalizations l10n) => switch (this) { _FeedTab.anime => l10n.feedTabAnime, _FeedTab.real => l10n.feedTabReal, _FeedTab.recommend => l10n.feedTabForYou, }; } class _TopIconButton extends StatelessWidget { const _TopIconButton({ super.key, required this.tooltip, required this.icon, required this.dimension, required this.glyphSize, required this.onPressed, }); final String tooltip; final IconData icon; final double dimension; final double glyphSize; final VoidCallback onPressed; @override Widget build(BuildContext context) { return SizedBox.square( dimension: dimension, child: IconButton( tooltip: tooltip, onPressed: onPressed, padding: EdgeInsets.zero, icon: Icon( icon, size: glyphSize, color: Colors.white, shadows: const [Shadow(blurRadius: 6, color: Colors.black)], ), ), ); } } /// 刷新入口:点击重新加载 feed,长按打开更多(清屏等)。 class _RefreshButton extends StatelessWidget { const _RefreshButton({ required this.tooltip, required this.dimension, required this.glyphSize, required this.refreshing, required this.onTap, required this.onLongPress, }); final String tooltip; final double dimension; final double glyphSize; final bool refreshing; final VoidCallback onTap; final VoidCallback onLongPress; @override Widget build(BuildContext context) { return Tooltip( message: tooltip, child: GestureDetector( key: const Key('home-feed-refresh'), behavior: HitTestBehavior.opaque, onTap: onTap, onLongPress: onLongPress, child: SizedBox.square( dimension: dimension, child: refreshing ? const Center( child: SizedBox.square( dimension: 18, child: CircularProgressIndicator( key: Key('home-feed-refreshing'), strokeWidth: 2, color: Colors.white, ), ), ) : Icon( Icons.refresh_rounded, size: glyphSize, color: Colors.white, shadows: const [Shadow(blurRadius: 6, color: Colors.black)], ), ), ), ); } } class _FeedTabItem extends StatelessWidget { const _FeedTabItem({ required this.tab, required this.label, required this.selected, required this.fontSize, required this.underlineWidth, required this.underlineHeight, required this.onTap, }); final _FeedTab tab; final String label; final bool selected; final double fontSize; final double underlineWidth; final double underlineHeight; final VoidCallback onTap; @override Widget build(BuildContext context) { return GestureDetector( key: Key('home-feed-tab-${tab.keyName}'), behavior: HitTestBehavior.opaque, onTap: onTap, child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( label, maxLines: 1, style: TextStyle( color: selected ? Colors.white : Colors.white70, fontSize: fontSize, fontWeight: selected ? FontWeight.w800 : FontWeight.w600, shadows: const [Shadow(blurRadius: 6, color: Colors.black)], ), ), const SizedBox(height: 4), Container( width: underlineWidth, height: underlineHeight, decoration: BoxDecoration( color: selected ? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular(1), ), ), ], ), ); } }