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((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); } } }