Files
FruitServer/docs/plans/2026-10-21-K95.1-seamless-first-switch.md

2.9 KiB
Raw Permalink Blame History

K95.1 首次切换频道无感显示

目标

  • 推荐首屏成功后后台预取 anime/live,避免第一次切换出现阻断式 loading。
  • 目标频道尚未返回时保留上一频道 PlayerPage 和视频画面,只叠加轻量 loading 遮罩。
  • 数据到达后原子替换为新频道 feed,并从第 1 个播放。

分支

  • ReelShort:fix/k95.1-seamless-first-switch,从已包含 K95 的 origin/develop 创建。
  • FruitServer 仅文档留痕,不改功能代码。

A. HomeFeedController

新增 Future<void> prefetchChannels():

  • 仅处理 anime/live。
  • 只对 !loaded && !loading 的频道请求。
  • 请求结果写回对应频道缓存。
  • 不切换当前频道、不改变当前列表;仅目标恰为当前频道时 notify。
  • 失败只写 lastError,不向外抛。
  • 成功预取设置一次性 skipNextRefresh:首次切到该频道直接使用缓存,不立刻重复静默刷新; 后续再次切回仍执行正常缓存静默刷新。
  • 预取期间用户切换到该频道时等待已有 inFlight,不重复请求。

B. HomePage / _HomeTabs

B1 最近可见 feed

_HomeTabsState 维护:

  • List<Drama>? _visibleDramas
  • HomeFeedChannel? _visibleChannel

当前频道有数据时更新这两个值,并以当前频道渲染 PlayerPage。

B2 首次切换

当前频道无数据且 loading:

  • 继续复用上一频道 _visibleDramas/_visibleChannel 渲染同一个 PlayerPage。
  • header 使用当前选中频道,但 PlayerPage.feedChannel 仍使用可见旧频道,避免队列更新。
  • Stack 上叠加 IgnorePointer + ColoredBox(black26) + 24px spinner, Key 为 home-feed-channel-loading。
  • 数据返回后页面树保持同一层级的 PlayerPage,传入新 feed/channel,触发原子更新和第 1 个起播。
  • 空结果/失败后才切到「暂无剧目 / 重试」占位。

B3 Prefetch 触发

推荐首屏后台刷新成功后 unawaited(prefetchChannels())。

C. 测试

HomeFeedController:

  • prefetch 后首次 select anime 不再发请求。
  • prefetch 期间选择 anime 不重复请求。
  • prefetch 失败不影响推荐,也不向外抛。

Widget:

  • 首切未缓存频道时 PlayerPage 仍在树中且出现 loading 遮罩。
  • 数据返回后遮罩消失并使用新频道 feed。

门禁:

  • dart analyze lib test
  • flutter test --no-pub
  • flutter build apk --debug

风险与控制

  1. 预取完成与 select 同时发生时,select 等待同一 inFlight,避免重复请求。
  2. 加载遮罩 IgnorePointer,不阻断顶栏/底栏交互。
  3. PlayerPage 在正常和 loading 状态都放在相同 Stack 层级,保证 State 不因分支切换被销毁。
  4. 不改后端、ReelShortWeb、specs;不安装或操作真机。

Git

  • 提交:fix(home): 首切频道预取并保留播放画面
  • --no-ff 合回 ReelShort develop 并 push。
  • 作者:Zhou Weidong <502333560@qq.com>;禁止 git add -A。