From fb4d1b78d6ce5369b16ca4fe9eb9116def22d1e5 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 30 Mar 2026 00:15:28 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E9=A2=84=E8=A7=88=E7=9A=84=E7=A8=B3=E5=AE=9A=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/features/home/home_screen.dart | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/lib/features/home/home_screen.dart b/lib/features/home/home_screen.dart index e51472e..e51aa7e 100644 --- a/lib/features/home/home_screen.dart +++ b/lib/features/home/home_screen.dart @@ -41,6 +41,8 @@ class _HomeScreenState extends State { /// [index] -> 最近一次 visibility 回调的 [VisibilityInfo.visibleFraction] final Map _cardVisibleFraction = {}; bool _visibilityReconcileScheduled = false; + /// 是否曾有过「可见比例 ≥ 阈值」的探测器回调(用于区分冷启动无回调 vs 已全部滑出视口) + bool _visibilityHadMeaningfulReport = false; /// IndexedStack 切回首页或 [homePlaybackResumeNonce] 递增后,让 [VisibilityDetector] 重新计算。 /// 单次 [notifyNow] 在「Modal 收起 / 子路由 pop」后首帧常过早,与 My Gallery 一致采用多帧 + 短延迟。 @@ -62,6 +64,17 @@ class _HomeScreenState extends State { }); Future.delayed(const Duration(milliseconds: 80), notify); Future.delayed(const Duration(milliseconds: 220), notify); + // 冷启动首屏 Grid 布局/Texture 较晚就绪时,前两档仍可能拿不到可见性 + Future.delayed(const Duration(milliseconds: 450), notify); + Future.delayed(const Duration(milliseconds: 720), notify); + Future.delayed(const Duration(milliseconds: 950), () { + if (!mounted || !widget.isActive) return; + VisibilityDetectorController.instance.notifyNow(); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted || !widget.isActive) return; + _seedFirstScreenVideoIndicesIfStillEmpty(); + }); + }); } @override @@ -76,6 +89,21 @@ class _HomeScreenState extends State { if (widget.isActive) refreshAccount(); } + void _seedFirstScreenVideoIndicesIfStillEmpty() { + if (!mounted || !widget.isActive) return; + if (_visibleVideoIndices.isNotEmpty) return; + if (_visibilityHadMeaningfulReport) return; + final tasks = _displayTasks; + if (tasks.isEmpty) return; + final seed = {}; + for (var i = 0; i < tasks.length && seed.length < _maxConcurrentHomeVideos; i++) { + final u = tasks[i].previewVideoUrl; + if (u != null && u.isNotEmpty) seed.add(i); + } + if (seed.isEmpty) return; + setState(() => _visibleVideoIndices = seed); + } + @override void dispose() { homePlaybackResumeNonce.removeListener(_onHomePlaybackResumeSignal); @@ -94,6 +122,10 @@ class _HomeScreenState extends State { void _onExtConfigChanged() { if (mounted) setState(() {}); + // 不用 Timer 防抖:连续 listener 会互相 cancel,首装可能永远不触发 notify + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && widget.isActive) _scheduleVisibilityRefresh(); + }); } void _onHomeReloadNonce() { @@ -110,6 +142,7 @@ class _HomeScreenState extends State { if (!mounted) return; if (info.visibleFraction >= _videoVisibilityThreshold) { _cardVisibleFraction[index] = info.visibleFraction; + _visibilityHadMeaningfulReport = true; } else { _cardVisibleFraction.remove(index); } @@ -133,6 +166,16 @@ class _HomeScreenState extends State { scored.add(MapEntry(i, e.value)); } } + if (scored.isEmpty) { + // 冷启动:探测器尚未给出 ≥ 阈值的回调时,不要用空集覆盖(否则会清掉兜底或一直不播) + if (!_visibilityHadMeaningfulReport) return; + _userPausedVideoIndices.clear(); + if (!_setsEqual(_visibleVideoIndices, {})) { + setState(() => _visibleVideoIndices = {}); + } + return; + } + scored.sort((a, b) => b.value.compareTo(a.value)); final next = scored .take(_maxConcurrentHomeVideos) @@ -255,6 +298,10 @@ class _HomeScreenState extends State { print('HomeScreen: Failed to load categories'); } setState(() => _categoriesLoading = false); + // 默认选中 pets 等不会走 [_loadTasks] 的路径,也要触发一次可见性(否则预览不自动播) + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && widget.isActive) _scheduleVisibilityRefresh(); + }); } } @@ -271,6 +318,7 @@ class _HomeScreenState extends State { _userPausedVideoIndices.clear(); _visibleVideoIndices = {}; _cardVisibleFraction.clear(); + _visibilityHadMeaningfulReport = false; }); } else { setState(() { @@ -278,6 +326,7 @@ class _HomeScreenState extends State { _userPausedVideoIndices.clear(); _visibleVideoIndices = {}; _cardVisibleFraction.clear(); + _visibilityHadMeaningfulReport = false; }); } setState(() => _tasksLoading = false); @@ -300,6 +349,7 @@ class _HomeScreenState extends State { _userPausedVideoIndices.clear(); _visibleVideoIndices = {}; _cardVisibleFraction.clear(); + _visibilityHadMeaningfulReport = false; }); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted && widget.isActive) _scheduleVisibilityRefresh();