初始化
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user