mod: 视频简介跳过加载转圈;修复优先展示评论时无分p和连播失效、切换分p后未刷新数据

This commit is contained in:
orz12
2024-07-21 10:35:03 +08:00
parent 9c93d11733
commit 4713a32730
2 changed files with 280 additions and 287 deletions

View File

@@ -43,7 +43,8 @@ class VideoIntroController extends GetxController {
Rx<VideoDetailData> videoDetail = VideoDetailData().obs; Rx<VideoDetailData> videoDetail = VideoDetailData().obs;
// up主粉丝数 // up主粉丝数
Map userStat = {'follower': '-'}; Rx<Map<String, dynamic>> userStat =
Rx<Map<String, dynamic>>({'follower': '-'});
// 是否点赞 // 是否点赞
RxBool hasLike = false.obs; RxBool hasLike = false.obs;
@@ -71,6 +72,8 @@ class VideoIntroController extends GetxController {
bool isPaused = false; bool isPaused = false;
String heroTag = ''; String heroTag = '';
late ModelResult modelResult; late ModelResult modelResult;
Rx<Map<String, dynamic>> queryVideoIntroData =
Rx<Map<String, dynamic>>({"status": true});
@override @override
void onInit() { void onInit() {
@@ -95,9 +98,9 @@ class VideoIntroController extends GetxController {
} }
videoItem!['title'] = str; videoItem!['title'] = str;
} }
videoItem!['stat'] = keys.contains('stat') && args.stat; videoItem!['stat'] = keys.contains('stat') ? args.stat : null;
videoItem!['pubdate'] = keys.contains('pubdate') && args.pubdate; videoItem!['pubdate'] = keys.contains('pubdate') ? args.pubdate : null;
videoItem!['owner'] = keys.contains('owner') && args.owner; videoItem!['owner'] = keys.contains('owner') ? args.owner : null;
} }
} }
userLogin = userInfo != null; userLogin = userInfo != null;
@@ -108,10 +111,11 @@ class VideoIntroController extends GetxController {
queryOnlineTotal(); queryOnlineTotal();
startTimer(); // 在页面加载时启动定时器 startTimer(); // 在页面加载时启动定时器
} }
queryVideoIntro();
} }
// 获取视频简介&分p // 获取视频简介&分p
Future queryVideoIntro() async { void queryVideoIntro() async {
var result = await VideoHttp.videoIntro(bvid: bvid); var result = await VideoHttp.videoIntro(bvid: bvid);
if (result['status']) { if (result['status']) {
videoDetail.value = result['data']!; videoDetail.value = result['data']!;
@@ -128,6 +132,7 @@ class VideoIntroController extends GetxController {
SmartDialog.showToast( SmartDialog.showToast(
"${result['code']} ${result['msg']} ${result['data']}"); "${result['code']} ${result['msg']} ${result['data']}");
} }
queryVideoIntroData.value = result;
if (userLogin) { if (userLogin) {
// 获取点赞状态 // 获取点赞状态
queryHasLikeVideo(); queryHasLikeVideo();
@@ -138,14 +143,15 @@ class VideoIntroController extends GetxController {
// //
queryFollowStatus(); queryFollowStatus();
} }
return result;
} }
// 获取up主粉丝数 // 获取up主粉丝数
Future queryUserStat() async { Future queryUserStat() async {
var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!); var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!);
if (result['status']) { if (result['status']) {
userStat = result['data']; print(result['data']);
userStat.value = result['data'];
userStat.refresh();
} }
} }
@@ -449,18 +455,22 @@ class VideoIntroController extends GetxController {
videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.danmakuCid.value = cid;
videoDetailCtr.queryVideoUrl(); videoDetailCtr.queryVideoUrl();
// 重新请求相关视频 // 重新请求相关视频
final RelatedController? relatedCtr = try {
Get.find<RelatedController?>(tag: heroTag); final RelatedController relatedCtr =
relatedCtr?.bvid = bvid; Get.find<RelatedController>(tag: heroTag);
relatedCtr?.queryRelatedVideo(); relatedCtr.bvid = bvid;
relatedCtr.queryRelatedVideo();
} catch (_) {}
// 重新请求评论 // 重新请求评论
final VideoReplyController? videoReplyCtr = try {
Get.find<VideoReplyController?>(tag: heroTag); final VideoReplyController videoReplyCtr =
videoReplyCtr?.aid = aid; Get.find<VideoReplyController>(tag: heroTag);
videoReplyCtr?.queryReplyList(type: 'init'); videoReplyCtr.aid = aid;
videoReplyCtr.queryReplyList(type: 'init');
} catch (_) {}
this.bvid = bvid; this.bvid = bvid;
lastPlayCid.value = cid; lastPlayCid.value = cid;
await queryVideoIntro(); queryVideoIntro();
} }
void startTimer() { void startTimer() {
@@ -580,9 +590,20 @@ class VideoIntroController extends GetxController {
} }
bool playRelated() { bool playRelated() {
final RelatedController relatedCtr = late RelatedController relatedCtr;
Get.find<RelatedController>(tag: heroTag); try {
if (relatedCtr.relatedVideoList.isEmpty) { relatedCtr = Get.find<RelatedController>(tag: heroTag);
if (relatedCtr.relatedVideoList.isEmpty) {
SmartDialog.showToast('暂无相关视频,停止连播');
return false;
}
} catch (_) {
relatedCtr = Get.put(RelatedController(), tag: heroTag);
relatedCtr.queryRelatedVideo().then((value) {
if (value['status']) {
playRelated();
}
});
return false; return false;
} }

View File

@@ -1,3 +1,5 @@
import 'dart:ffi';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -40,7 +42,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
late String heroTag; late String heroTag;
late VideoIntroController videoIntroController; late VideoIntroController videoIntroController;
VideoDetailData? videoDetail; VideoDetailData? videoDetail;
late Future? _futureBuilderFuture; // late Future? _futureBuilderFuture;
// 添加页面缓存 // 添加页面缓存
@override @override
@@ -56,7 +58,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
// } // }
heroTag = widget.heroTag; heroTag = widget.heroTag;
videoIntroController = Get.put(VideoIntroController(), tag: heroTag); videoIntroController = Get.put(VideoIntroController(), tag: heroTag);
_futureBuilderFuture = videoIntroController.queryVideoIntro(); // _futureBuilderFuture = videoIntroController.queryVideoIntro();
videoIntroController.videoDetail.listen((value) { videoIntroController.videoDetail.listen((value) {
videoDetail = value; videoDetail = value;
}); });
@@ -71,53 +73,19 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return FutureBuilder( return Obx(() => videoIntroController.videoDetail.value.title == null
future: _futureBuilderFuture, ? VideoInfo(
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return const SliverToBoxAdapter(child: SizedBox());
}
if (snapshot.data['status']) {
// 请求成功
return Obx(
() => VideoInfo(
loadingStatus: false,
videoDetail: videoIntroController.videoDetail.value,
heroTag: heroTag,
),
);
} else {
// 请求错误
return HttpError(
errMsg: snapshot.data['msg'],
btnText: snapshot.data['code'] == -404 ||
snapshot.data['code'] == 62002
? '上一页'
: null,
fn: () {
if (snapshot.data['code'] == -404 ||
snapshot.data['code'] == 62002) {
Get.back();
return;
}
_futureBuilderFuture = videoIntroController.queryVideoIntro();
_futureBuilderFuture!.then((value) {
videoIntroController.videoDetail.refresh();
setState(() {});
});
},
);
}
} else {
return VideoInfo(
loadingStatus: true, loadingStatus: true,
videoDetail: videoDetail, videoDetail: videoDetail,
heroTag: heroTag, heroTag: heroTag,
); )
} : VideoInfo(
}, //key:herotag
); key: ValueKey(heroTag),
loadingStatus: false,
videoDetail: videoIntroController.videoDetail.value,
heroTag: heroTag,
));
} }
} }
@@ -145,9 +113,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
late final bool loadingStatus; // 加载状态 late final bool loadingStatus; // 加载状态
late final dynamic owner;
late final dynamic follower;
late final dynamic followStatus;
late int mid; late int mid;
late String memberHeroTag; late String memberHeroTag;
late bool enableAi; late bool enableAi;
@@ -171,11 +136,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
videoItem = videoIntroController.videoItem!; videoItem = videoIntroController.videoItem!;
loadingStatus = widget.loadingStatus; loadingStatus = widget.loadingStatus;
owner = loadingStatus ? videoItem['owner'] : widget.videoDetail!.owner;
follower = loadingStatus
? '-'
: Utils.numFormat(videoIntroController.userStat['follower']);
followStatus = videoIntroController.followStatus;
enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true);
} }
@@ -268,219 +228,227 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
right: StyleString.safeSpace, right: StyleString.safeSpace,
top: 10), top: 10),
sliver: SliverToBoxAdapter( sliver: SliverToBoxAdapter(
child: !loadingStatus child: Column(
? Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Row(children: [
Row(children: [ Expanded(
child: GestureDetector(
onTap: onPushMember,
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 1, horizontal: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
NetworkImgLayer(
type: 'avatar',
src: loadingStatus
? videoItem['owner']?.face ?? ""
: widget.videoDetail!.owner!.face,
width: 30,
height: 30,
fadeInDuration: Duration.zero,
fadeOutDuration: Duration.zero,
),
const SizedBox(width: 10),
Expanded( Expanded(
child: GestureDetector( child: Column(
onTap: onPushMember, crossAxisAlignment: CrossAxisAlignment.start,
child: Container( children: [
padding: const EdgeInsets.symmetric( Text(
vertical: 1, horizontal: 0), loadingStatus
child: Row( ? videoItem['owner']?.name ?? ""
mainAxisAlignment: MainAxisAlignment.center, : widget.videoDetail!.owner!.name,
mainAxisSize: MainAxisSize.min, maxLines: 1,
children: [ overflow: TextOverflow.ellipsis,
NetworkImgLayer( style: TextStyle(
type: 'avatar', fontSize: 12, color: t.colorScheme.primary),
src: loadingStatus // semanticsLabel: "Up主${owner.name}",
? owner.face ),
: widget.videoDetail!.owner!.face, const SizedBox(height: 0),
width: 30, Obx(() => Text(
height: 30, Utils.numFormat(videoIntroController
fadeInDuration: Duration.zero, .userStat.value['follower']),
fadeOutDuration: Duration.zero, semanticsLabel:
), "${Utils.numFormat(videoIntroController.userStat.value['follower'])}粉丝",
const SizedBox(width: 10), style: TextStyle(
Expanded( fontSize: 12,
child: Column( color: t.colorScheme.outline,
crossAxisAlignment: CrossAxisAlignment.start, ),
children: [
Text(
owner.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
color: t.colorScheme.primary),
// semanticsLabel: "Up主${owner.name}",
),
const SizedBox(height: 0),
Text(
follower,
semanticsLabel: "$follower粉丝",
style: TextStyle(
fontSize: 12,
color: t.colorScheme.outline,
),
),
],
)), )),
followButton(context, t), ],
],
),
),
)), )),
if (isHorizontal) ...[ followButton(context, t),
const SizedBox(width: 10),
Expanded(
child: actionGrid(context, videoIntroController)),
]
]),
const SizedBox(height: 8),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => showIntroDetail(),
child: Row(children: [
Expanded(
child: Text(
!loadingStatus
? widget.videoDetail!.title
: videoItem['title'],
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
)),
Icon(
Icons.arrow_forward_ios,
size: 16,
color: t.colorScheme.outline,
),
]),
),
Stack(
children: [
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => showIntroDetail(),
child: Padding(
padding: const EdgeInsets.only(top: 7, bottom: 6),
child: Row(
children: <Widget>[
StatView(
theme: 'gray',
view: !loadingStatus
? widget.videoDetail!.stat!.view
: videoItem['stat'].view,
size: 'medium',
),
const SizedBox(width: 10),
StatDanMu(
theme: 'gray',
danmu: !loadingStatus
? widget.videoDetail!.stat!.danmu
: videoItem['stat'].danmu,
size: 'medium',
),
const SizedBox(width: 10),
Text(
Utils.dateFormat(
!loadingStatus
? widget.videoDetail!.pubdate
: videoItem['pubdate'],
formatType: 'detail'),
style: TextStyle(
fontSize: 12,
color: t.colorScheme.outline,
),
),
if (MineController.anonymity) ...<Widget>[
const SizedBox(width: 10),
Icon(
MdiIcons.incognito,
size: 15,
color: t.colorScheme.outline,
semanticLabel: '无痕',
),
],
const SizedBox(width: 10),
if (videoIntroController.isShowOnlineTotal)
Obx(
() => Text(
'${videoIntroController.total.value}人在看',
style: TextStyle(
fontSize: 12,
color: t.colorScheme.outline,
),
),
),
],
),
),
),
if (enableAi)
Positioned(
right: 10,
top: 6,
child: Semantics(
label: 'AI总结',
child: GestureDetector(
onTap: () async {
final res = await videoIntroController
.aiConclusion();
if (res['status']) {
showAiBottomSheet();
}
},
child: Image.asset('assets/images/ai.png',
height: 22),
)),
)
],
),
// 点赞收藏转发 布局样式1
// SingleChildScrollView(
// padding: const EdgeInsets.only(top: 7, bottom: 7),
// scrollDirection: Axis.horizontal,
// child: actionRow(
// context,
// videoIntroController,
// videoDetailCtr,
// ),
// ),
// 点赞收藏转发 布局样式2
if (!isHorizontal)
actionGrid(context, videoIntroController),
// 合集
if (!loadingStatus &&
widget.videoDetail!.ugcSeason != null) ...[
Obx(
() => SeasonPanel(
heroTag: heroTag,
ugcSeason: widget.videoDetail!.ugcSeason!,
cid: videoIntroController.lastPlayCid.value != 0
? videoIntroController.lastPlayCid.value
: widget.videoDetail!.pages!.first.cid,
changeFuc: videoIntroController.changeSeasonOrbangu,
),
)
], ],
if (!loadingStatus &&
widget.videoDetail!.pages != null &&
widget.videoDetail!.pages!.length > 1) ...[
Obx(() => PagesPanel(
heroTag: heroTag,
pages: widget.videoDetail!.pages!,
cid: videoIntroController.lastPlayCid.value,
bvid: videoIntroController.bvid,
changeFuc:
videoIntroController.changeSeasonOrbangu,
))
],
],
)
: const SizedBox(
height: 130,
child: Center(
child: CircularProgressIndicator(),
), ),
), ),
), )),
if (isHorizontal) ...[
const SizedBox(width: 10),
Expanded(child: actionGrid(context, videoIntroController)),
]
]),
const SizedBox(height: 8),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => showIntroDetail(),
child: Row(children: [
Expanded(
child: Text(
widget.videoDetail?.title ?? videoItem['title'] ?? "",
// !loadingStatus
// ? "${widget.videoDetail?.title}"
// : videoItem['title'] ?? "",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
)),
Icon(
Icons.arrow_forward_ios,
size: 16,
color: t.colorScheme.outline,
),
]),
),
Stack(
children: [
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => showIntroDetail(),
child: Padding(
padding: const EdgeInsets.only(top: 7, bottom: 6),
child: Row(
children: <Widget>[
StatView(
theme: 'gray',
view: !loadingStatus
? widget.videoDetail?.stat?.view ?? '-'
: videoItem['stat']?.view ?? '-',
size: 'medium',
),
const SizedBox(width: 10),
StatDanMu(
theme: 'gray',
danmu: !loadingStatus
? widget.videoDetail?.stat?.danmu ?? '-'
: videoItem['stat']?.danmu ?? '-',
size: 'medium',
),
const SizedBox(width: 10),
Text(
Utils.dateFormat(
!loadingStatus
? widget.videoDetail?.pubdate
: videoItem['pubdate'],
formatType: 'detail'),
style: TextStyle(
fontSize: 12,
color: t.colorScheme.outline,
),
),
if (MineController.anonymity) ...<Widget>[
const SizedBox(width: 10),
Icon(
MdiIcons.incognito,
size: 15,
color: t.colorScheme.outline,
semanticLabel: '无痕',
),
],
const SizedBox(width: 10),
if (videoIntroController.isShowOnlineTotal)
Obx(
() => Text(
'${videoIntroController.total.value}人在看',
style: TextStyle(
fontSize: 12,
color: t.colorScheme.outline,
),
),
),
],
),
),
),
if (enableAi)
Positioned(
right: 10,
top: 6,
child: Semantics(
label: 'AI总结',
child: GestureDetector(
onTap: () async {
final res =
await videoIntroController.aiConclusion();
if (res['status']) {
showAiBottomSheet();
}
},
child:
Image.asset('assets/images/ai.png', height: 22),
)),
)
],
),
Obx(
() => videoIntroController.queryVideoIntroData.value["status"]
? const SizedBox()
: Center(
child: TextButton.icon(
icon: const Icon(Icons.refresh),
onPressed: () {
videoIntroController
.queryVideoIntroData.value["status"] = true;
videoIntroController.queryVideoIntro();
},
label: const Text("点此重新加载"),
),
),
),
// 点赞收藏转发 布局样式1
// SingleChildScrollView(
// padding: const EdgeInsets.only(top: 7, bottom: 7),
// scrollDirection: Axis.horizontal,
// child: actionRow(
// context,
// videoIntroController,
// videoDetailCtr,
// ),
// ),
// 点赞收藏转发 布局样式2
if (!isHorizontal) actionGrid(context, videoIntroController),
// 合集
if (!loadingStatus && widget.videoDetail?.ugcSeason != null) ...[
Obx(
() => SeasonPanel(
heroTag: heroTag,
ugcSeason: widget.videoDetail!.ugcSeason!,
cid: videoIntroController.lastPlayCid.value != 0
? videoIntroController.lastPlayCid.value
: widget.videoDetail!.pages!.first.cid,
changeFuc: videoIntroController.changeSeasonOrbangu,
),
)
],
if (!loadingStatus &&
widget.videoDetail?.pages != null &&
widget.videoDetail!.pages!.length > 1) ...[
Obx(() => PagesPanel(
heroTag: heroTag,
pages: widget.videoDetail!.pages!,
cid: videoIntroController.lastPlayCid.value,
bvid: videoIntroController.bvid,
changeFuc: videoIntroController.changeSeasonOrbangu,
))
],
],
)),
); );
}, },
); );
@@ -494,15 +462,19 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
tapTargetSize: MaterialTapTargetSize.shrinkWrap, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: const EdgeInsets.only(left: 6, right: 6), padding: const EdgeInsets.only(left: 6, right: 6),
foregroundColor: (followStatus?['attribute'] ?? 0) != 0 foregroundColor:
? t.colorScheme.outline (videoIntroController.followStatus['attribute'] ?? 0) != 0
: t.colorScheme.onPrimary, ? t.colorScheme.outline
backgroundColor: (followStatus?['attribute'] ?? 0) != 0 : t.colorScheme.onPrimary,
? t.colorScheme.onInverseSurface backgroundColor:
: t.colorScheme.primary, // 设置按钮背景色 (videoIntroController.followStatus['attribute'] ?? 0) != 0
? t.colorScheme.onInverseSurface
: t.colorScheme.primary, // 设置按钮背景色
), ),
child: Text( child: Text(
((followStatus?['attribute'] ?? 0) != 0) ? '已关注' : '关注', ((videoIntroController.followStatus['attribute'] ?? 0) != 0)
? '已关注'
: '关注',
style: TextStyle(fontSize: t.textTheme.labelMedium!.fontSize), style: TextStyle(fontSize: t.textTheme.labelMedium!.fontSize),
), ),
), ),