110 lines
3.5 KiB
Dart
110 lines
3.5 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:hgdj/hj_utils/const.dart';
|
|
import 'package:hgdj/tools_base/event_bus/events.dart';
|
|
import 'package:hgdj/tools_base/widget/scroll_positioned_list/src/scrollable_positioned_list.dart';
|
|
|
|
import '../../../hj_model/acg/comic_chapters_model.dart';
|
|
import '../../../hj_model/cartoon_media_info.dart';
|
|
import '../../../hj_utils/api_service/acg_service.dart';
|
|
import '../../hj_utils/history_util.dart';
|
|
import '../../tools_base/event_bus/event_bus_util.dart';
|
|
import 'cartoon_read_page.dart';
|
|
import 'widget/acg_source_manager.dart';
|
|
import 'widget/cartoon_subset_alert.dart';
|
|
|
|
class CartoonDetailLogic extends GetxController
|
|
with GetTickerProviderStateMixin {
|
|
String? id;
|
|
CartoonMediaInfo? mediaInfo;
|
|
|
|
bool showHeader = true;
|
|
double showOffset = 245;
|
|
bool get isCartoonType => manager.mediaInfo?.mediaType == 'image'; //是否是动画
|
|
|
|
// 推荐 tab 固定 3 个(漫画/视频/动漫),顺序由页面按当前内容类型动态排
|
|
late TabController tabCtr = TabController(length: 3, vsync: this);
|
|
|
|
late ScrollController scrollCtr = ScrollController()
|
|
..addListener(() {
|
|
//滚过阈值切换顶部沉浸 header,状态变了才刷新
|
|
final show = scrollCtr.offset <= showOffset;
|
|
if (show != showHeader) {
|
|
showHeader = show;
|
|
update(['header']);
|
|
}
|
|
});
|
|
final ItemScrollController itemSCtrl = ItemScrollController();
|
|
late ACGSourceManager manager;
|
|
late StreamSubscription subscription;
|
|
|
|
CartoonDetailLogic({this.id})
|
|
: manager = ACGSourceManager(id ?? ''); //初始化,加载子集
|
|
|
|
@override
|
|
void onReady() async {
|
|
super.onReady();
|
|
// subscription 是 late,必须在任何 await 前初始化,
|
|
// 否则详情加载途中退出会让 onClose 的 cancel() 打到未初始化字段而崩
|
|
subscription = eventBus.on<ACGMenuChanged>((event) {
|
|
//列表未 attach(未 build/布局完)时 jumpTo 内部会 ! 取空崩溃,加保护
|
|
if (itemSCtrl.isAttached) {
|
|
itemSCtrl.jumpTo(index: event.index ?? 0);
|
|
}
|
|
});
|
|
// 先拿详情(含 freeEpisode),再拉子集,才能正确标记前 N 集免费
|
|
await loadData();
|
|
manager.fetchAllEpisodes();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
subscription.cancel();
|
|
eventBus.off(subscription);
|
|
tabCtr.dispose(); //修复泄漏
|
|
scrollCtr.dispose(); //修复泄漏
|
|
// manager 由页面的 ChangeNotifierProvider(create:) 在卸载时 dispose,本类不再重复释放
|
|
super.onClose();
|
|
}
|
|
|
|
//获取媒体详情
|
|
Future loadData() async {
|
|
mediaInfo = await ACGService.getMediaInfo(id ?? '');
|
|
if (mediaInfo != null) {
|
|
HistoryUtil.insert(mediaInfo!, MediaStyle.Comics);
|
|
manager.mediaInfo = mediaInfo;
|
|
showOffset = mediaInfo?.mediaType == 'text' ? 300 : 320;
|
|
} else {
|
|
mediaInfo = CartoonMediaInfo();
|
|
}
|
|
update();
|
|
}
|
|
|
|
//切换集数
|
|
changeEpisode(int episode, bool jump) async {
|
|
manager.changeEpisodesIndex(episode, jump: jump);
|
|
play(); //播放
|
|
}
|
|
|
|
play() async {
|
|
//判断权限
|
|
ComicChapterInfo? info =
|
|
await manager.getEpisodeHasPermisson(manager.index);
|
|
if (info == null) return;
|
|
Get.to(() => CartoonReadPage(manager: manager..index = manager.index));
|
|
}
|
|
|
|
//集数弹窗
|
|
onMenuAction() async {
|
|
int episode = await Get.bottomSheet(
|
|
CartoonSubSetAlert(sourceManager: manager),
|
|
barrierColor: Colors.black.withValues(alpha: .2),
|
|
);
|
|
if (episode > -1) {
|
|
changeEpisode(episode, true);
|
|
}
|
|
}
|
|
}
|