feat(home): 首页按内容类型分频道 Feed 并支持分页

This commit is contained in:
Zhou Weidong committed 2026-10-21 17:33:52 +08:00
1 parent 4d0be47ee3
commit ae61d9ec4c
22 files changed
+1001 -71

No files matched your search

+29 -44
View File
@@ -2,6 +2,7 @@
import 'package:flutter/material.dart';
import '../../home/home_feed_channel.dart';
import '../../../l10n/app_localizations.dart';
const double _topIconSize = 36;
@@ -16,12 +17,16 @@ class HomeFeedHeader extends StatefulWidget {
const HomeFeedHeader({
super.key,
required this.l10n,
required this.channel,
required this.onChannelSelected,
required this.onRefresh,
required this.onSearch,
required this.onMore,
});
final AppLocalizations l10n;
final HomeFeedChannel channel;
final ValueChanged<HomeFeedChannel> onChannelSelected;
final Future<bool> Function() onRefresh;
final VoidCallback onSearch;
final VoidCallback onMore;
@@ -31,7 +36,6 @@ class HomeFeedHeader extends StatefulWidget {
}
class _HomeFeedHeaderState extends State<HomeFeedHeader> {
_FeedTab _tab = _FeedTab.recommend;
bool _refreshing = false;
@override
@@ -54,8 +58,12 @@ class _HomeFeedHeaderState extends State<HomeFeedHeader> {
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (var index = 0; index < _FeedTab.values.length; index++)
_tabSlot(_FeedTab.values[index], index),
for (
var index = 0;
index < HomeFeedChannel.values.length;
index++
)
_tabSlot(HomeFeedChannel.values[index], index),
],
),
),
@@ -74,15 +82,15 @@ class _HomeFeedHeaderState extends State<HomeFeedHeader> {
);
}
Widget _tabSlot(_FeedTab tab, int index) {
Widget _tabSlot(HomeFeedChannel channel, int index) {
final item = _FeedTabItem(
tab: tab,
label: tab.label(widget.l10n),
selected: _tab == tab,
channel: channel,
label: _label(channel),
selected: widget.channel == channel,
fontSize: _tabFontSize,
underlineWidth: _underlineWidth,
underlineHeight: _underlineHeight,
onTap: () => _selectTab(tab),
onTap: () => widget.onChannelSelected(channel),
);
if (index == 0) return item;
return Padding(
@@ -91,14 +99,6 @@ class _HomeFeedHeaderState extends State<HomeFeedHeader> {
);
}
void _selectTab(_FeedTab tab) {
if (tab.comingSoon) {
_showComingSoon();
return;
}
setState(() => _tab = tab);
}
Future<void> _refresh() async {
if (_refreshing) return;
setState(() => _refreshing = true);
@@ -106,31 +106,10 @@ class _HomeFeedHeaderState extends State<HomeFeedHeader> {
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,
String _label(HomeFeedChannel channel) => switch (channel) {
HomeFeedChannel.anime => widget.l10n.feedTabAnime,
HomeFeedChannel.live => widget.l10n.feedTabReal,
HomeFeedChannel.recommend => widget.l10n.feedTabForYou,
};
}
@@ -223,7 +202,7 @@ class _RefreshButton extends StatelessWidget {
class _FeedTabItem extends StatelessWidget {
const _FeedTabItem({
required this.tab,
required this.channel,
required this.label,
required this.selected,
required this.fontSize,
@@ -232,7 +211,7 @@ class _FeedTabItem extends StatelessWidget {
required this.onTap,
});
final _FeedTab tab;
final HomeFeedChannel channel;
final String label;
final bool selected;
final double fontSize;
@@ -243,7 +222,7 @@ class _FeedTabItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
key: Key('home-feed-tab-${tab.keyName}'),
key: Key('home-feed-tab-${_keyName(channel)}'),
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Column(
@@ -272,4 +251,10 @@ class _FeedTabItem extends StatelessWidget {
),
);
}
String _keyName(HomeFeedChannel channel) => switch (channel) {
HomeFeedChannel.anime => 'anime',
HomeFeedChannel.live => 'real',
HomeFeedChannel.recommend => 'recommend',
};
}