优化:卡片预览的稳定性
This commit is contained in:
parent
2ae2c19024
commit
fb4d1b78d6
@ -41,6 +41,8 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
/// [index] -> 最近一次 visibility 回调的 [VisibilityInfo.visibleFraction]
|
||||
final Map<int, double> _cardVisibleFraction = {};
|
||||
bool _visibilityReconcileScheduled = false;
|
||||
/// 是否曾有过「可见比例 ≥ 阈值」的探测器回调(用于区分冷启动无回调 vs 已全部滑出视口)
|
||||
bool _visibilityHadMeaningfulReport = false;
|
||||
|
||||
/// IndexedStack 切回首页或 [homePlaybackResumeNonce] 递增后,让 [VisibilityDetector] 重新计算。
|
||||
/// 单次 [notifyNow] 在「Modal 收起 / 子路由 pop」后首帧常过早,与 My Gallery 一致采用多帧 + 短延迟。
|
||||
@ -62,6 +64,17 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
});
|
||||
Future<void>.delayed(const Duration(milliseconds: 80), notify);
|
||||
Future<void>.delayed(const Duration(milliseconds: 220), notify);
|
||||
// 冷启动首屏 Grid 布局/Texture 较晚就绪时,前两档仍可能拿不到可见性
|
||||
Future<void>.delayed(const Duration(milliseconds: 450), notify);
|
||||
Future<void>.delayed(const Duration(milliseconds: 720), notify);
|
||||
Future<void>.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<HomeScreen> {
|
||||
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 = <int>{};
|
||||
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<HomeScreen> {
|
||||
|
||||
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<HomeScreen> {
|
||||
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<HomeScreen> {
|
||||
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<HomeScreen> {
|
||||
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<HomeScreen> {
|
||||
_userPausedVideoIndices.clear();
|
||||
_visibleVideoIndices = {};
|
||||
_cardVisibleFraction.clear();
|
||||
_visibilityHadMeaningfulReport = false;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
@ -278,6 +326,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
_userPausedVideoIndices.clear();
|
||||
_visibleVideoIndices = {};
|
||||
_cardVisibleFraction.clear();
|
||||
_visibilityHadMeaningfulReport = false;
|
||||
});
|
||||
}
|
||||
setState(() => _tasksLoading = false);
|
||||
@ -300,6 +349,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
_userPausedVideoIndices.clear();
|
||||
_visibleVideoIndices = {};
|
||||
_cardVisibleFraction.clear();
|
||||
_visibilityHadMeaningfulReport = false;
|
||||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted && widget.isActive) _scheduleVisibilityRefresh();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user