feat: 合集封面展示、封面切换
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
import '../models/common/video_episode_type.dart';
|
import '../models/common/video_episode_type.dart';
|
||||||
|
|
||||||
@ -44,25 +45,38 @@ class EpisodeBottomSheet {
|
|||||||
title = '第${episode.title}话 ${episode.longTitle!}';
|
title = '第${episode.title}话 ${episode.longTitle!}';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ListTile(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SmartDialog.showToast('切换至「$title」');
|
SmartDialog.showToast('切换至「$title」');
|
||||||
changeFucCall.call(episode, index);
|
changeFucCall.call(episode, index);
|
||||||
},
|
},
|
||||||
dense: false,
|
child: Padding(
|
||||||
leading: isCurrentIndex
|
padding: const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8),
|
||||||
? Image.asset(
|
child: isFullScreen
|
||||||
'assets/images/live.gif',
|
? Text(
|
||||||
color: primary,
|
|
||||||
height: 12,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
title: Text(
|
|
||||||
title,
|
title,
|
||||||
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: isCurrentIndex ? primary : onSurface,
|
color: isCurrentIndex ? primary : onSurface,
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(width: 130, height: 75, src: episode.cover),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
maxLines: 2,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: isCurrentIndex ? primary : onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ class HotVideoItemModel {
|
|||||||
this.dimension,
|
this.dimension,
|
||||||
this.shortLinkV2,
|
this.shortLinkV2,
|
||||||
this.firstFrame,
|
this.firstFrame,
|
||||||
|
this.cover,
|
||||||
this.pubLocation,
|
this.pubLocation,
|
||||||
this.seasontype,
|
this.seasontype,
|
||||||
this.isOgv,
|
this.isOgv,
|
||||||
@ -50,6 +51,7 @@ class HotVideoItemModel {
|
|||||||
Dimension? dimension;
|
Dimension? dimension;
|
||||||
String? shortLinkV2;
|
String? shortLinkV2;
|
||||||
String? firstFrame;
|
String? firstFrame;
|
||||||
|
String? cover;
|
||||||
String? pubLocation;
|
String? pubLocation;
|
||||||
int? seasontype;
|
int? seasontype;
|
||||||
bool? isOgv;
|
bool? isOgv;
|
||||||
@ -77,6 +79,7 @@ class HotVideoItemModel {
|
|||||||
dimension = Dimension.fromMap(json['dimension']);
|
dimension = Dimension.fromMap(json['dimension']);
|
||||||
shortLinkV2 = json["short_link_v2"];
|
shortLinkV2 = json["short_link_v2"];
|
||||||
firstFrame = json["first_frame"];
|
firstFrame = json["first_frame"];
|
||||||
|
cover = json["first_frame"];
|
||||||
pubLocation = json["pub_location"];
|
pubLocation = json["pub_location"];
|
||||||
seasontype = json["seasontype"];
|
seasontype = json["seasontype"];
|
||||||
isOgv = json["isOgv"];
|
isOgv = json["isOgv"];
|
||||||
|
|||||||
@ -382,6 +382,7 @@ class Part {
|
|||||||
String? weblink;
|
String? weblink;
|
||||||
Dimension? dimension;
|
Dimension? dimension;
|
||||||
String? firstFrame;
|
String? firstFrame;
|
||||||
|
String? cover;
|
||||||
|
|
||||||
Part({
|
Part({
|
||||||
this.cid,
|
this.cid,
|
||||||
@ -393,6 +394,7 @@ class Part {
|
|||||||
this.weblink,
|
this.weblink,
|
||||||
this.dimension,
|
this.dimension,
|
||||||
this.firstFrame,
|
this.firstFrame,
|
||||||
|
this.cover,
|
||||||
});
|
});
|
||||||
|
|
||||||
fromRawJson(String str) => Part.fromJson(json.decode(str));
|
fromRawJson(String str) => Part.fromJson(json.decode(str));
|
||||||
@ -411,6 +413,7 @@ class Part {
|
|||||||
? null
|
? null
|
||||||
: Dimension.fromJson(json["dimension"]);
|
: Dimension.fromJson(json["dimension"]);
|
||||||
firstFrame = json["first_frame"];
|
firstFrame = json["first_frame"];
|
||||||
|
cover = json["first_frame"];
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
@ -634,6 +637,7 @@ class EpisodeItem {
|
|||||||
this.attribute,
|
this.attribute,
|
||||||
this.page,
|
this.page,
|
||||||
this.bvid,
|
this.bvid,
|
||||||
|
this.cover,
|
||||||
});
|
});
|
||||||
int? seasonId;
|
int? seasonId;
|
||||||
int? sectionId;
|
int? sectionId;
|
||||||
@ -644,6 +648,7 @@ class EpisodeItem {
|
|||||||
int? attribute;
|
int? attribute;
|
||||||
Part? page;
|
Part? page;
|
||||||
String? bvid;
|
String? bvid;
|
||||||
|
String? cover;
|
||||||
|
|
||||||
EpisodeItem.fromJson(Map<String, dynamic> json) {
|
EpisodeItem.fromJson(Map<String, dynamic> json) {
|
||||||
seasonId = json['season_id'];
|
seasonId = json['season_id'];
|
||||||
@ -655,6 +660,7 @@ class EpisodeItem {
|
|||||||
attribute = json['attribute'];
|
attribute = json['attribute'];
|
||||||
page = Part.fromJson(json['page']);
|
page = Part.fromJson(json['page']);
|
||||||
bvid = json['bvid'];
|
bvid = json['bvid'];
|
||||||
|
cover = json['arc']['pic'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -215,7 +215,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 修改分P或番剧分集
|
// 修改分P或番剧分集
|
||||||
Future changeSeasonOrbangu(bvid, cid, aid) async {
|
Future changeSeasonOrbangu(bvid, cid, aid, cover) async {
|
||||||
// 重新获取视频资源
|
// 重新获取视频资源
|
||||||
VideoDetailController videoDetailCtr =
|
VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
||||||
@ -223,6 +223,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
videoDetailCtr.cid.value = cid;
|
videoDetailCtr.cid.value = cid;
|
||||||
videoDetailCtr.danmakuCid.value = cid;
|
videoDetailCtr.danmakuCid.value = cid;
|
||||||
videoDetailCtr.oid.value = aid;
|
videoDetailCtr.oid.value = aid;
|
||||||
|
videoDetailCtr.cover.value = cover;
|
||||||
videoDetailCtr.queryVideoUrl();
|
videoDetailCtr.queryVideoUrl();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
try {
|
try {
|
||||||
@ -281,7 +282,8 @@ class BangumiIntroController extends GetxController {
|
|||||||
int cid = episodes[nextIndex].cid!;
|
int cid = episodes[nextIndex].cid!;
|
||||||
String bvid = episodes[nextIndex].bvid!;
|
String bvid = episodes[nextIndex].bvid!;
|
||||||
int aid = episodes[nextIndex].aid!;
|
int aid = episodes[nextIndex].aid!;
|
||||||
changeSeasonOrbangu(bvid, cid, aid);
|
String cover = episodes[nextIndex].cover!;
|
||||||
|
changeSeasonOrbangu(bvid, cid, aid, cover);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 播放器底栏 选集 回调
|
// 播放器底栏 选集 回调
|
||||||
@ -302,7 +304,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
sheetHeight: Get.size.height,
|
sheetHeight: Get.size.height,
|
||||||
isFullScreen: true,
|
isFullScreen: true,
|
||||||
changeFucCall: (item, index) {
|
changeFucCall: (item, index) {
|
||||||
changeSeasonOrbangu(item.bvid, item.cid, item.aid);
|
changeSeasonOrbangu(item.bvid, item.cid, item.aid, item.cover);
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
},
|
},
|
||||||
).buildShowContent(Get.context!),
|
).buildShowContent(Get.context!),
|
||||||
|
|||||||
@ -322,8 +322,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
pages: widget.bangumiDetail!.episodes!,
|
pages: widget.bangumiDetail!.episodes!,
|
||||||
cid: cid! ?? widget.bangumiDetail!.episodes!.first.cid!,
|
cid: cid! ?? widget.bangumiDetail!.episodes!.first.cid!,
|
||||||
sheetHeight: sheetHeight,
|
sheetHeight: sheetHeight,
|
||||||
changeFuc: (bvid, cid, aid) =>
|
changeFuc: (bvid, cid, aid, cover) => bangumiIntroController
|
||||||
bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid),
|
.changeSeasonOrbangu(bvid, cid, aid, cover),
|
||||||
bangumiDetail: bangumiIntroController.bangumiDetail.value,
|
bangumiDetail: bangumiIntroController.bangumiDetail.value,
|
||||||
bangumiIntroController: bangumiIntroController,
|
bangumiIntroController: bangumiIntroController,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -84,11 +84,12 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
item.bvid,
|
item.bvid,
|
||||||
item.cid,
|
item.cid,
|
||||||
item.aid,
|
item.aid,
|
||||||
|
item.cover,
|
||||||
);
|
);
|
||||||
if (_bottomSheetController != null) {
|
if (_bottomSheetController != null) {
|
||||||
_bottomSheetController?.close();
|
_bottomSheetController?.close();
|
||||||
}
|
}
|
||||||
currentIndex = i;
|
currentIndex.value = i;
|
||||||
scrollToIndex();
|
scrollToIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -73,6 +73,7 @@ class VideoDetailController extends GetxController
|
|||||||
ReplyItemModel? firstFloor;
|
ReplyItemModel? firstFloor;
|
||||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
RxString bgCover = ''.obs;
|
RxString bgCover = ''.obs;
|
||||||
|
RxString cover = ''.obs;
|
||||||
PlPlayerController plPlayerController = PlPlayerController.getInstance();
|
PlPlayerController plPlayerController = PlPlayerController.getInstance();
|
||||||
|
|
||||||
late VideoItem firstVideo;
|
late VideoItem firstVideo;
|
||||||
@ -120,10 +121,12 @@ class VideoDetailController extends GetxController
|
|||||||
var args = argMap['videoItem'];
|
var args = argMap['videoItem'];
|
||||||
if (args.pic != null && args.pic != '') {
|
if (args.pic != null && args.pic != '') {
|
||||||
videoItem['pic'] = args.pic;
|
videoItem['pic'] = args.pic;
|
||||||
|
cover.value = args.pic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (keys.contains('pic')) {
|
if (keys.contains('pic')) {
|
||||||
videoItem['pic'] = argMap['pic'];
|
videoItem['pic'] = argMap['pic'];
|
||||||
|
cover.value = argMap['pic'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tabCtr = TabController(length: 2, vsync: this);
|
tabCtr = TabController(length: 2, vsync: this);
|
||||||
|
|||||||
@ -432,7 +432,7 @@ class VideoIntroController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 修改分P或番剧分集
|
// 修改分P或番剧分集
|
||||||
Future changeSeasonOrbangu(bvid, cid, aid) async {
|
Future changeSeasonOrbangu(bvid, cid, aid, cover) async {
|
||||||
// 重新获取视频资源
|
// 重新获取视频资源
|
||||||
final VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
@ -447,6 +447,7 @@ class VideoIntroController extends GetxController {
|
|||||||
videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid);
|
videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid);
|
||||||
videoDetailCtr.cid.value = cid;
|
videoDetailCtr.cid.value = cid;
|
||||||
videoDetailCtr.danmakuCid.value = cid;
|
videoDetailCtr.danmakuCid.value = cid;
|
||||||
|
videoDetailCtr.cover.value = cover;
|
||||||
videoDetailCtr.queryVideoUrl();
|
videoDetailCtr.queryVideoUrl();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
try {
|
try {
|
||||||
@ -494,6 +495,7 @@ class VideoIntroController extends GetxController {
|
|||||||
void nextPlay() {
|
void nextPlay() {
|
||||||
final List episodes = [];
|
final List episodes = [];
|
||||||
bool isPages = false;
|
bool isPages = false;
|
||||||
|
late String cover;
|
||||||
if (videoDetail.value.ugcSeason != null) {
|
if (videoDetail.value.ugcSeason != null) {
|
||||||
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
|
||||||
final List<SectionItem> sections = ugcSeason.sections!;
|
final List<SectionItem> sections = ugcSeason.sections!;
|
||||||
@ -510,6 +512,7 @@ class VideoIntroController extends GetxController {
|
|||||||
final int currentIndex =
|
final int currentIndex =
|
||||||
episodes.indexWhere((e) => e.cid == lastPlayCid.value);
|
episodes.indexWhere((e) => e.cid == lastPlayCid.value);
|
||||||
int nextIndex = currentIndex + 1;
|
int nextIndex = currentIndex + 1;
|
||||||
|
cover = episodes[nextIndex].cover;
|
||||||
final VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||||
@ -526,7 +529,7 @@ class VideoIntroController extends GetxController {
|
|||||||
final int cid = episodes[nextIndex].cid!;
|
final int cid = episodes[nextIndex].cid!;
|
||||||
final String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
|
final String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
|
||||||
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
|
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
|
||||||
changeSeasonOrbangu(rBvid, cid, rAid);
|
changeSeasonOrbangu(rBvid, cid, rAid, cover);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置关注分组
|
// 设置关注分组
|
||||||
@ -591,10 +594,11 @@ class VideoIntroController extends GetxController {
|
|||||||
isFullScreen: true,
|
isFullScreen: true,
|
||||||
changeFucCall: (item, index) {
|
changeFucCall: (item, index) {
|
||||||
if (dataType == VideoEpidoesType.videoEpisode) {
|
if (dataType == VideoEpidoesType.videoEpisode) {
|
||||||
changeSeasonOrbangu(IdUtils.av2bv(item.aid), item.cid, item.aid);
|
changeSeasonOrbangu(
|
||||||
|
IdUtils.av2bv(item.aid), item.cid, item.aid, item.cover);
|
||||||
}
|
}
|
||||||
if (dataType == VideoEpidoesType.videoPart) {
|
if (dataType == VideoEpidoesType.videoPart) {
|
||||||
changeSeasonOrbangu(bvid, item.cid, null);
|
changeSeasonOrbangu(bvid, item.cid, null, item.cover);
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -383,11 +383,12 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
? videoIntroController.lastPlayCid.value
|
? videoIntroController.lastPlayCid.value
|
||||||
: widget.videoDetail!.pages!.first.cid,
|
: widget.videoDetail!.pages!.first.cid,
|
||||||
sheetHeight: videoDetailCtr.sheetHeight.value,
|
sheetHeight: videoDetailCtr.sheetHeight.value,
|
||||||
changeFuc: (bvid, cid, aid) =>
|
changeFuc: (bvid, cid, aid, cover) =>
|
||||||
videoIntroController.changeSeasonOrbangu(
|
videoIntroController.changeSeasonOrbangu(
|
||||||
bvid,
|
bvid,
|
||||||
cid,
|
cid,
|
||||||
aid,
|
aid,
|
||||||
|
cover,
|
||||||
),
|
),
|
||||||
videoIntroCtr: videoIntroController,
|
videoIntroCtr: videoIntroController,
|
||||||
),
|
),
|
||||||
@ -401,10 +402,12 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
pages: widget.videoDetail!.pages!,
|
pages: widget.videoDetail!.pages!,
|
||||||
cid: videoIntroController.lastPlayCid.value,
|
cid: videoIntroController.lastPlayCid.value,
|
||||||
sheetHeight: videoDetailCtr.sheetHeight.value,
|
sheetHeight: videoDetailCtr.sheetHeight.value,
|
||||||
changeFuc: (cid) => videoIntroController.changeSeasonOrbangu(
|
changeFuc: (cid, cover) =>
|
||||||
|
videoIntroController.changeSeasonOrbangu(
|
||||||
videoIntroController.bvid,
|
videoIntroController.bvid,
|
||||||
cid,
|
cid,
|
||||||
null,
|
null,
|
||||||
|
cover,
|
||||||
),
|
),
|
||||||
videoIntroCtr: videoIntroController,
|
videoIntroCtr: videoIntroController,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -58,7 +58,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void changeFucCall(item, i) async {
|
void changeFucCall(item, i) async {
|
||||||
widget.changeFuc?.call(item.cid);
|
widget.changeFuc?.call(item.cid, item.cover);
|
||||||
currentIndex.value = i;
|
currentIndex.value = i;
|
||||||
_bottomSheetController?.close();
|
_bottomSheetController?.close();
|
||||||
scrollToIndex();
|
scrollToIndex();
|
||||||
@ -129,7 +129,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 35,
|
height: 55,
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
@ -163,7 +163,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.pages[i].pagePart!,
|
widget.pages[i].pagePart!,
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: isCurrentIndex
|
color: isCurrentIndex
|
||||||
|
|||||||
@ -67,6 +67,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
|||||||
IdUtils.av2bv(item.aid),
|
IdUtils.av2bv(item.aid),
|
||||||
item.cid,
|
item.cid,
|
||||||
item.aid,
|
item.aid,
|
||||||
|
item.cover,
|
||||||
);
|
);
|
||||||
currentIndex.value = i;
|
currentIndex.value = i;
|
||||||
_bottomSheetController?.close();
|
_bottomSheetController?.close();
|
||||||
|
|||||||
@ -458,13 +458,19 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
handlePlay();
|
handlePlay();
|
||||||
},
|
},
|
||||||
|
child: Obx(
|
||||||
|
() => AnimatedOpacity(
|
||||||
|
duration: const Duration(milliseconds: 100), // 渐变动画的持续时间
|
||||||
|
opacity: 1, // 设置不透明度
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
type: 'emote',
|
type: 'emote',
|
||||||
src: vdCtr.videoItem['pic'],
|
src: vdCtr.cover.value,
|
||||||
width: Get.width,
|
width: Get.width,
|
||||||
height: videoHeight.value,
|
height: videoHeight.value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user