Files
huangguo_android/lib/hj_page/drama/drama_feed_logic.dart
T
2026-09-15 15:44:13 +07:00

103 lines
3.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/hj_model/drama/drama_models.dart';
import 'package:hgdj/hj_model/video_model.dart';
import 'package:hgdj/tools_base/cache/history/drama_resume_store.dart';
import 'package:hgdj/hj_utils/api_service/drama_service.dart';
import 'package:hgdj/tools_base/debug_log.dart';
import 'package:uuid/uuid.dart';
/// AI短剧信息流:一条 = 一部剧的第 1 集,竖划换下一部剧。
/// 排序、去重、已看沉底全在服务端,**没有页码**——每次请求就是下一批,客户端不得二次排序,
/// 也不做二次去重(服务端目前会重发同一部剧,去重就意味着列表不增长、用户直接划不动)
class DramaFeedLogic extends GetxController {
// GetBuilder 局部刷新 id:当前页变化时只刷信息流
static const idFeed = 'feed';
// ===== 外部传入 =====
final TabController? superTabCtr; // 外层频道 tab,用于切到本 tab 时补加载
final int? tabIndex;
DramaFeedLogic({this.superTabCtr, this.tabIndex});
// ===== 状态 =====
bool isFirstLoaded = false; // 首屏是否加载完成(false 时显示 loading
bool isLoading = false; // 防并发翻页
bool hasNext = true;
/// 本批的幂等键:网络重试沿用,拿到结果才作废
String? _requestId;
// ===== 数据 =====
List<VideoModel>? list;
// ===== 交互态 =====
int pageIndex = 0; // 当前显示第几条
// ===== Controller =====
late PageController pageCtr;
final ValueNotifier<bool> isScrolling = ValueNotifier(false); // 上下滑动切剧时隐藏进度条
@override
void onInit() {
super.onInit();
pageCtr = PageController(initialPage: pageIndex);
superTabCtr?.addListener(_onTabChanged);
//续播位置是本地记的,进来就把表读上来。不用 await:读的是本地 sqflite,比首批网络请求快得多,
//播放器起播时(要等 /media_content/info 回来)必然已就绪
dramaResume.load();
loadData();
}
@override
void onClose() {
superTabCtr?.removeListener(_onTabChanged);
pageCtr.dispose();
isScrolling.dispose();
super.onClose();
}
// 切到本 tab 且还没数据就补一次
void _onTabChanged() {
if (superTabCtr?.index == tabIndex && list?.isEmpty == true) loadData();
}
/// [isRefresh] 下拉刷新时清空重来,否则往后追加
Future<void> loadData({bool isRefresh = true}) async {
if (isLoading) return;
if (!isRefresh && !hasNext) return;
isLoading = true;
try {
final resp = await DramaService.fetchFeed(
size: 20, requestId: _requestId ??= const Uuid().v4());
if (resp != null) _requestId = null; // 成功才作废;失败重试沿用同一个 id,避免服务端当成两批
//拿到什么收什么,不按剧 id 去重:服务端反复下发同一部剧时,去重会让列表不增长,
//用户停在最后一条划不动、onPageChanged 也不再触发,就彻底卡死了。
//宁可刷到重复的剧,也不能卡住——重复的 key 由页面用「剧 id + 下标」避开
final more = <VideoModel>[];
for (final item in resp?.list ?? const <DramaFeedItem>[]) {
final media = item.media;
if (media == null) continue; // 缺 media 的脏数据,别在信息流里留个空位
more.add(media.toVideoModel(item.content));
}
hasNext = resp?.hasNext ?? false;
list = isRefresh ? more : [...?list, ...more];
} catch (e) {
list ??= [];
debugLog('短剧信息流获取失败:$e');
}
isLoading = false;
isFirstLoaded = true;
update();
}
void onPageChanged(int index) {
if (pageIndex != index) {
pageIndex = index;
update([idFeed]); // 只刷信息流,各 item 重算 isCurrentPage
}
// 快到底了先把下一批拉上,别等滑到最后一条才请求
if (hasNext && (list?.length ?? 0) - index <= 6) loadData(isRefresh: false);
}
}