fix(home): 首切频道预取并保留播放画面

This commit is contained in:
Zhou Weidong committed 2026-10-21 17:53:58 +08:00
1 parent dfa79bed88
commit 677e49fa6e
5 files changed
+242 -27

No files matched your search

+76 -21
View File
@@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../../core/config/app_config.dart';
import '../../core/data/models/drama.dart';
import '../../core/data/services/home_feed_controller.dart';
import '../../core/data/services/catalog_controller.dart';
import '../../core/data/services/catalog_retry.dart';
@@ -141,7 +142,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
_recommendSeeded = true;
homeFeed.seedRecommend(catalog.dramas);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) unawaited(homeFeed.refreshCurrent());
if (mounted) unawaited(_refreshRecommendAndPrefetch(homeFeed));
});
}
return _HomeTabs(key: const Key('home-tabs'), feedController: homeFeed);
@@ -151,6 +152,12 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
return _homeFeed ??= HomeFeedController(catalog.repository);
}
Future<void> _refreshRecommendAndPrefetch(HomeFeedController homeFeed) async {
if (await homeFeed.refreshCurrent()) {
await homeFeed.prefetchChannels();
}
}
Widget _startupSplash() {
final isAndroid = defaultTargetPlatform == TargetPlatform.android;
final size = isAndroid ? _androidSplashSize : _defaultSplashSize;
@@ -222,11 +229,15 @@ class _HomeTabs extends StatefulWidget {
class _HomeTabsState extends State<_HomeTabs> {
late final HomeLogic logic;
List<Drama>? _visibleDramas;
HomeFeedChannel? _visibleChannel;
@override
void initState() {
super.initState();
logic = HomeBinding().create(widget.feedController.dramas);
_visibleDramas = widget.feedController.dramas;
_visibleChannel = widget.feedController.channel;
widget.feedController.addListener(_onFeedChanged);
}
@@ -291,26 +302,70 @@ class _HomeTabsState extends State<_HomeTabs> {
}
Widget _home(BuildContext context) {
final dramas = widget.feedController.dramas;
if (dramas.isEmpty) return _feedChannelPlaceholder(context);
return PlayerPage(
homeMode: true,
embedded: true,
active: logic.tab == 0,
drama: dramas.first,
initialIndex: 0,
feedDramas: dramas,
feedChannel: widget.feedController.channel,
onOpenFullPlayer: (drama, episode) =>
logic.playDrama(context, drama, episodeId: episode.id),
onRefreshFeed: widget.feedController.refreshCurrent,
onLoadMore: widget.feedController.loadMore,
onChannelSelected: _selectFeedChannel,
onDramaChanged: logic.onDramaChanged,
onImmersiveChanged: logic.onImmersiveChanged,
onSearch: () => logic.select(1, search: true),
onDiscover: () => logic.select(1),
onMy: () => logic.select(3),
final currentDramas = widget.feedController.dramas;
final currentChannel = widget.feedController.channel;
if (currentDramas.isNotEmpty) {
_visibleDramas = currentDramas;
_visibleChannel = currentChannel;
}
if (currentDramas.isEmpty && !widget.feedController.loadingFirstPage) {
return _feedChannelPlaceholder(context);
}
final visibleDramas = _visibleDramas;
final visibleChannel = _visibleChannel;
final switching =
currentDramas.isEmpty && widget.feedController.loadingFirstPage;
if (visibleDramas == null ||
visibleDramas.isEmpty ||
visibleChannel == null) {
return _feedChannelPlaceholder(context);
}
final playerDramas = currentDramas.isEmpty ? visibleDramas : currentDramas;
final playerChannel = currentDramas.isEmpty
? visibleChannel
: currentChannel;
return Stack(
fit: StackFit.expand,
children: [
PlayerPage(
homeMode: true,
embedded: true,
active: logic.tab == 0,
drama: playerDramas.first,
initialIndex: 0,
feedDramas: playerDramas,
feedChannel: playerChannel,
headerChannel: currentChannel,
onOpenFullPlayer: (drama, episode) =>
logic.playDrama(context, drama, episodeId: episode.id),
onRefreshFeed: widget.feedController.refreshCurrent,
onLoadMore: widget.feedController.loadMore,
onChannelSelected: _selectFeedChannel,
onDramaChanged: logic.onDramaChanged,
onImmersiveChanged: logic.onImmersiveChanged,
onSearch: () => logic.select(1, search: true),
onDiscover: () => logic.select(1),
onMy: () => logic.select(3),
),
if (switching)
Positioned.fill(
child: IgnorePointer(
key: const Key('home-feed-channel-loading'),
child: ColoredBox(
color: Colors.black26,
child: Center(
child: SizedBox.square(
dimension: 24,
child: CircularProgressIndicator(
strokeWidth: 2.4,
color: Colors.white70,
),
),
),
),
),
),
],
);
}
+6 -1
View File
@@ -59,6 +59,7 @@ class PlayerPage extends StatefulWidget {
this.onMenu,
this.feedDramas,
this.feedChannel,
this.headerChannel,
this.onDramaChanged,
this.onImmersiveChanged,
this.onOpenFullPlayer,
@@ -82,6 +83,7 @@ class PlayerPage extends StatefulWidget {
final VoidCallback? onMenu;
final List<Drama>? feedDramas;
final HomeFeedChannel? feedChannel;
final HomeFeedChannel? headerChannel;
final ValueChanged<Drama>? onDramaChanged;
final ValueChanged<bool>? onImmersiveChanged;
final Future<void> Function(Drama drama, Episode episode)? onOpenFullPlayer;
@@ -295,7 +297,10 @@ class _PlayerPageState extends State<PlayerPage>
bottom: false,
child: HomeFeedHeader(
l10n: l10n,
channel: widget.feedChannel ?? HomeFeedChannel.recommend,
channel:
widget.headerChannel ??
widget.feedChannel ??
HomeFeedChannel.recommend,
onChannelSelected: widget.onChannelSelected ?? (_) {},
onRefresh: () async {
final ok = await widget.onRefreshFeed?.call() ?? true;