1
This commit is contained in:
@@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../core/log/action_logger.dart';
|
||||
import '../../core/storage/feed_progress_store.dart';
|
||||
import '../../core/theme/app_theme.dart';
|
||||
import '../../models/video_item.dart';
|
||||
import '../../providers/feed_provider.dart';
|
||||
@@ -30,10 +31,25 @@ class _FeedScreenState extends ConsumerState<FeedScreen>
|
||||
final PageController _pageController = PageController();
|
||||
StreamSubscription<FeedEvent>? _eventSub;
|
||||
|
||||
/// 已完成首屏位置同步的策略(冷启动续播跳页只需一次)
|
||||
final Set<FeedStrategy> _initialSyncDone = {};
|
||||
|
||||
/// 定时兜底保存播放进度(被杀进程时也能续播)
|
||||
Timer? _progressTimer;
|
||||
|
||||
/// App 是否在前台(退后台后不写进度)
|
||||
bool _appActive = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
// 冷启动恢复上次所在的 Tab(口碑/热播等),再按该策略的进度续播
|
||||
final lastName = FeedProgressStore.I.readLastStrategy();
|
||||
_strategy = FeedStrategy.values.firstWhere(
|
||||
(s) => s.name == lastName && s != FeedStrategy.search,
|
||||
orElse: () => FeedStrategy.recommend,
|
||||
);
|
||||
// 登记当前策略,HomeShell 切回首页时只恢复这个策略的播放器池
|
||||
ref.read(activeFeedStrategyProvider.notifier).state = _strategy;
|
||||
// 监听计费/UI 事件(弹窗与 Toast 由 Notifier 事件流驱动)
|
||||
@@ -46,6 +62,11 @@ class _FeedScreenState extends ConsumerState<FeedScreen>
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) ref.read(feedProvider(_strategy).notifier).rebindCurrent();
|
||||
});
|
||||
_progressTimer = Timer.periodic(const Duration(seconds: 5), (_) {
|
||||
if (!mounted || !_appActive) return;
|
||||
if (!ref.read(feedTabVisibleProvider)) return;
|
||||
unawaited(ref.read(feedProvider(_strategy).notifier).persistProgress());
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -78,6 +99,7 @@ class _FeedScreenState extends ConsumerState<FeedScreen>
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
routeObserver.unsubscribe(this);
|
||||
_progressTimer?.cancel();
|
||||
_eventSub?.cancel();
|
||||
// 离开 Feed 路由(如跳登录页):彻底停止播放(断音+断缓存下载)。
|
||||
// feedProvider 是全局常驻,不处理则播放器池在后台继续出声
|
||||
@@ -93,10 +115,15 @@ class _FeedScreenState extends ConsumerState<FeedScreen>
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
final notifier = ref.read(feedProvider(_strategy).notifier);
|
||||
if (state == AppLifecycleState.paused) {
|
||||
_appActive = false;
|
||||
// 先存进度再 suspend(suspend 会 stop 槽位,位置随即清零)
|
||||
unawaited(notifier.persistProgress());
|
||||
notifier.pool.suspend();
|
||||
} else if (state == AppLifecycleState.inactive) {
|
||||
unawaited(notifier.persistProgress());
|
||||
notifier.pool.pauseCurrent();
|
||||
} else if (state == AppLifecycleState.resumed) {
|
||||
_appActive = true;
|
||||
// 仅在首页 Tab 可见时恢复(在"我的"等页面回前台不播)
|
||||
if (ref.read(feedTabVisibleProvider)) {
|
||||
unawaited(() async {
|
||||
@@ -121,9 +148,16 @@ class _FeedScreenState extends ConsumerState<FeedScreen>
|
||||
|
||||
void _switchStrategy(FeedStrategy next) {
|
||||
if (next == _strategy) return;
|
||||
// 先保存旧策略进度再挂起(suspend 会 stop 槽位,位置随即清零)
|
||||
unawaited(ref.read(feedProvider(_strategy).notifier).persistProgress());
|
||||
// 挂起旧策略池(防在途 open 完成后自动出声)
|
||||
ref.read(feedProvider(_strategy).notifier).pool.suspend();
|
||||
unawaited(FeedProgressStore.I.saveLastStrategy(next.name));
|
||||
setState(() => _strategy = next);
|
||||
// 会话内已有数据的策略由下方跳页逻辑处理,不再走冷启动位置同步
|
||||
if (ref.read(feedProvider(next)).items.isNotEmpty) {
|
||||
_initialSyncDone.add(next);
|
||||
}
|
||||
ref.read(activeFeedStrategyProvider.notifier).state = next;
|
||||
_eventSub?.cancel();
|
||||
_eventSub = ref
|
||||
@@ -175,6 +209,22 @@ class _FeedScreenState extends ConsumerState<FeedScreen>
|
||||
final state = ref.watch(feedProvider(_strategy));
|
||||
final notifier = ref.read(feedProvider(_strategy).notifier);
|
||||
|
||||
// 冷启动续播:列表加载完成后把 PageView 定位到恢复的下标(每个策略一次)。
|
||||
// Notifier 会在 refresh 时按上次 videoId 恢复 currentIndex,此处只负责跳页,
|
||||
// 跳页触发的 onPageChanged 会把新列表与播放器池对齐
|
||||
ref.listen<FeedState>(feedProvider(_strategy), (previous, next) {
|
||||
if (next.items.isEmpty) return;
|
||||
if (!_initialSyncDone.add(_strategy)) return;
|
||||
final target = next.currentIndex;
|
||||
if (target <= 0) return;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted || !_pageController.hasClients) return;
|
||||
if ((_pageController.page?.round() ?? 0) != target) {
|
||||
_pageController.jumpToPage(target);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: Stack(
|
||||
|
||||
Reference in New Issue
Block a user