diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index bdc23e57..d204473d 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -43,7 +43,8 @@ class VideoIntroController extends GetxController { Rx videoDetail = VideoDetailData().obs; // up主粉丝数 - Map userStat = {'follower': '-'}; + Rx> userStat = + Rx>({'follower': '-'}); // 是否点赞 RxBool hasLike = false.obs; @@ -71,6 +72,8 @@ class VideoIntroController extends GetxController { bool isPaused = false; String heroTag = ''; late ModelResult modelResult; + Rx> queryVideoIntroData = + Rx>({"status": true}); @override void onInit() { @@ -95,9 +98,9 @@ class VideoIntroController extends GetxController { } videoItem!['title'] = str; } - videoItem!['stat'] = keys.contains('stat') && args.stat; - videoItem!['pubdate'] = keys.contains('pubdate') && args.pubdate; - videoItem!['owner'] = keys.contains('owner') && args.owner; + videoItem!['stat'] = keys.contains('stat') ? args.stat : null; + videoItem!['pubdate'] = keys.contains('pubdate') ? args.pubdate : null; + videoItem!['owner'] = keys.contains('owner') ? args.owner : null; } } userLogin = userInfo != null; @@ -108,10 +111,11 @@ class VideoIntroController extends GetxController { queryOnlineTotal(); startTimer(); // 在页面加载时启动定时器 } + queryVideoIntro(); } // 获取视频简介&分p - Future queryVideoIntro() async { + void queryVideoIntro() async { var result = await VideoHttp.videoIntro(bvid: bvid); if (result['status']) { videoDetail.value = result['data']!; @@ -128,6 +132,7 @@ class VideoIntroController extends GetxController { SmartDialog.showToast( "${result['code']} ${result['msg']} ${result['data']}"); } + queryVideoIntroData.value = result; if (userLogin) { // 获取点赞状态 queryHasLikeVideo(); @@ -138,14 +143,15 @@ class VideoIntroController extends GetxController { // queryFollowStatus(); } - return result; } // 获取up主粉丝数 Future queryUserStat() async { var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!); 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.queryVideoUrl(); // 重新请求相关视频 - final RelatedController? relatedCtr = - Get.find(tag: heroTag); - relatedCtr?.bvid = bvid; - relatedCtr?.queryRelatedVideo(); + try { + final RelatedController relatedCtr = + Get.find(tag: heroTag); + relatedCtr.bvid = bvid; + relatedCtr.queryRelatedVideo(); + } catch (_) {} // 重新请求评论 - final VideoReplyController? videoReplyCtr = - Get.find(tag: heroTag); - videoReplyCtr?.aid = aid; - videoReplyCtr?.queryReplyList(type: 'init'); + try { + final VideoReplyController videoReplyCtr = + Get.find(tag: heroTag); + videoReplyCtr.aid = aid; + videoReplyCtr.queryReplyList(type: 'init'); + } catch (_) {} this.bvid = bvid; lastPlayCid.value = cid; - await queryVideoIntro(); + queryVideoIntro(); } void startTimer() { @@ -580,9 +590,20 @@ class VideoIntroController extends GetxController { } bool playRelated() { - final RelatedController relatedCtr = - Get.find(tag: heroTag); - if (relatedCtr.relatedVideoList.isEmpty) { + late RelatedController relatedCtr; + try { + relatedCtr = Get.find(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; } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 6b209680..a191291e 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,5 @@ +import 'dart:ffi'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -40,7 +42,7 @@ class _VideoIntroPanelState extends State late String heroTag; late VideoIntroController videoIntroController; VideoDetailData? videoDetail; - late Future? _futureBuilderFuture; + // late Future? _futureBuilderFuture; // 添加页面缓存 @override @@ -56,7 +58,7 @@ class _VideoIntroPanelState extends State // } heroTag = widget.heroTag; videoIntroController = Get.put(VideoIntroController(), tag: heroTag); - _futureBuilderFuture = videoIntroController.queryVideoIntro(); + // _futureBuilderFuture = videoIntroController.queryVideoIntro(); videoIntroController.videoDetail.listen((value) { videoDetail = value; }); @@ -71,53 +73,19 @@ class _VideoIntroPanelState extends State @override Widget build(BuildContext context) { super.build(context); - return FutureBuilder( - future: _futureBuilderFuture, - 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( + return Obx(() => videoIntroController.videoDetail.value.title == null + ? VideoInfo( loadingStatus: true, videoDetail: videoDetail, heroTag: heroTag, - ); - } - }, - ); + ) + : VideoInfo( + //key:herotag + key: ValueKey(heroTag), + loadingStatus: false, + videoDetail: videoIntroController.videoDetail.value, + heroTag: heroTag, + )); } } @@ -145,9 +113,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late final bool loadingStatus; // 加载状态 - late final dynamic owner; - late final dynamic follower; - late final dynamic followStatus; late int mid; late String memberHeroTag; late bool enableAi; @@ -171,11 +136,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { videoItem = videoIntroController.videoItem!; 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); } @@ -268,219 +228,227 @@ class _VideoInfoState extends State with TickerProviderStateMixin { right: StyleString.safeSpace, top: 10), sliver: SliverToBoxAdapter( - child: !loadingStatus - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row(children: [ + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + 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( - 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 - ? owner.face - : widget.videoDetail!.owner!.face, - width: 30, - height: 30, - fadeInDuration: Duration.zero, - fadeOutDuration: Duration.zero, - ), - const SizedBox(width: 10), - Expanded( - child: Column( - 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, - ), - ), - ], + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loadingStatus + ? videoItem['owner']?.name ?? "" + : widget.videoDetail!.owner!.name, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, color: t.colorScheme.primary), + // semanticsLabel: "Up主:${owner.name}", + ), + const SizedBox(height: 0), + Obx(() => Text( + Utils.numFormat(videoIntroController + .userStat.value['follower']), + semanticsLabel: + "${Utils.numFormat(videoIntroController.userStat.value['follower'])}粉丝", + style: TextStyle( + fontSize: 12, + color: t.colorScheme.outline, + ), )), - followButton(context, t), - ], - ), - ), + ], )), - 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( - !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: [ - 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) ...[ - 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, - ), - ) + followButton(context, t), ], - 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: [ + 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) ...[ + 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 with TickerProviderStateMixin { visualDensity: VisualDensity.compact, tapTargetSize: MaterialTapTargetSize.shrinkWrap, padding: const EdgeInsets.only(left: 6, right: 6), - foregroundColor: (followStatus?['attribute'] ?? 0) != 0 - ? t.colorScheme.outline - : t.colorScheme.onPrimary, - backgroundColor: (followStatus?['attribute'] ?? 0) != 0 - ? t.colorScheme.onInverseSurface - : t.colorScheme.primary, // 设置按钮背景色 + foregroundColor: + (videoIntroController.followStatus['attribute'] ?? 0) != 0 + ? t.colorScheme.outline + : t.colorScheme.onPrimary, + backgroundColor: + (videoIntroController.followStatus['attribute'] ?? 0) != 0 + ? t.colorScheme.onInverseSurface + : t.colorScheme.primary, // 设置按钮背景色 ), child: Text( - ((followStatus?['attribute'] ?? 0) != 0) ? '已关注' : '关注', + ((videoIntroController.followStatus['attribute'] ?? 0) != 0) + ? '已关注' + : '关注', style: TextStyle(fontSize: t.textTheme.labelMedium!.fontSize), ), ),