diff --git a/lib/http/api.dart b/lib/http/api.dart index 3db3c617..75a121ac 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -97,8 +97,8 @@ class Api { // 操作用户关系 static const String relationMod = '/x/relation/modify'; - // 相互关系查询 - static const String relationSearch = '/x/space/wbi/acc/relation'; + // 相互关系查询 // 失效 + // static const String relationSearch = '/x/space/wbi/acc/relation'; // 评论列表 // https://api.bilibili.com/x/v2/reply/main?csrf=6e22efc1a47225ea25f901f922b5cfdd&mode=3&oid=254175381&pagination_str=%7B%22offset%22:%22%22%7D&plat=1&seek_rpid=0&type=11 diff --git a/lib/http/user.dart b/lib/http/user.dart index 5a9cb593..45da72e4 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -251,29 +251,43 @@ class UserHttp { } } - // 相互关系查询 - static Future relationSearch(int mid) async { - Map params = await WbiSign().makSign({ - 'mid': mid, - 'web_location': 333.999, - }); + static Future hasFollow(int mid) async { var res = await Request().get( - Api.relationSearch, + Api.hasFollow, data: { - 'mid': mid, - 'w_rid': params['w_rid'], - 'wts': params['wts'], - 'web_location': 333.999, + 'fid': mid, }, ); if (res.data['code'] == 0) { - // relation 主动状态 - // 被动状态 return {'status': true, 'data': res.data['data']}; } else { return {'status': false, 'msg': res.data['message']}; } } + // // 相互关系查询 + // static Future relationSearch(int mid) async { + // Map params = await WbiSign().makSign({ + // 'mid': mid, + // 'token': '', + // 'platform': 'web', + // 'web_location': 1550101, + // }); + // var res = await Request().get( + // Api.relationSearch, + // data: { + // 'mid': mid, + // 'w_rid': params['w_rid'], + // 'wts': params['wts'], + // }, + // ); + // if (res.data['code'] == 0) { + // // relation 主动状态 + // // 被动状态 + // return {'status': true, 'data': res.data['data']}; + // } else { + // return {'status': false, 'msg': res.data['message']}; + // } + // } // 搜索历史记录 static Future searchHistory( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 62842075..63c66515 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -121,7 +121,14 @@ class _BangumiInfoState extends State { late final BangumiInfoModel? bangumiItem; late double sheetHeight; int? cid; - + bool isProcessing = false; + void Function()? handleState(Future Function() action) { + return isProcessing ? null : () async { + setState(() => isProcessing = true); + await action(); + setState(() => isProcessing = false); + }; + } @override void initState() { super.initState(); @@ -395,7 +402,7 @@ class _BangumiInfoState extends State { () => ActionItem( icon: const Icon(FontAwesomeIcons.thumbsUp), selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), - onTap: () => bangumiIntroController.actionLikeVideo(), + onTap: handleState(bangumiIntroController.actionLikeVideo), selectStatus: bangumiIntroController.hasLike.value, loadingStatus: false, text: !widget.loadingStatus @@ -406,7 +413,7 @@ class _BangumiInfoState extends State { () => ActionItem( icon: const Icon(FontAwesomeIcons.b), selectIcon: const Icon(FontAwesomeIcons.b), - onTap: () => bangumiIntroController.actionCoinVideo(), + onTap: handleState(bangumiIntroController.actionCoinVideo), selectStatus: bangumiIntroController.hasCoin.value, loadingStatus: false, text: !widget.loadingStatus @@ -455,7 +462,7 @@ class _BangumiInfoState extends State { Obx( () => ActionRowItem( icon: const Icon(FontAwesomeIcons.thumbsUp), - onTap: () => videoIntroController.actionLikeVideo(), + onTap: handleState(videoIntroController.actionLikeVideo), selectStatus: videoIntroController.hasLike.value, loadingStatus: widget.loadingStatus, text: !widget.loadingStatus @@ -467,7 +474,7 @@ class _BangumiInfoState extends State { Obx( () => ActionRowItem( icon: const Icon(FontAwesomeIcons.b), - onTap: () => videoIntroController.actionCoinVideo(), + onTap: handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin.value, loadingStatus: widget.loadingStatus, text: !widget.loadingStatus diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 38d09e04..c7d627a8 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -3,74 +3,56 @@ import 'package:pilipala/models/danmaku/dm.pb.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; class PlDanmakuController { - PlDanmakuController(this.cid, this.playerController); + PlDanmakuController(this.cid); final int cid; - final PlPlayerController playerController; - late Duration videoDuration; - // 按 6min 分段 - int segCount = 0; - List dmSegList = []; + Map> dmSegMap = {}; // 已请求的段落标记 - List hasrequestSeg = []; - int currentSegIndex = 1; - int currentDmIndex = 0; + List requestedSeg = []; - void calcSegment() { - dmSegList.clear(); - // 视频分段数 - segCount = (videoDuration.inSeconds / (60 * 6)).ceil(); - dmSegList = List.generate( - segCount < 1 ? 1 : segCount, (index) => DmSegMobileReply()); - // 当前分段 - try { - currentSegIndex = - (playerController.position.value.inSeconds / (60 * 6)).ceil(); - currentSegIndex = currentSegIndex < 1 ? 1 : currentSegIndex; - } catch (_) {} + bool get initiated => requestedSeg.isNotEmpty; + + static int SEGMENT_LENGTH = 60 * 6 * 1000; + + void initiate(int videoDuration, int progress) { + if (requestedSeg.isEmpty) { + int segCount = (videoDuration / SEGMENT_LENGTH).ceil(); + requestedSeg = List.generate(segCount, (index) => false); + } + queryDanmaku( + calcSegment(progress) + ); } - Future> queryDanmaku() async { - // dmSegList.clear(); + void dispose() { + dmSegMap.clear(); + requestedSeg.clear(); + } + + int calcSegment(int progress) { + return progress ~/ SEGMENT_LENGTH; + } + + void queryDanmaku(int segmentIndex) async { + assert(requestedSeg[segmentIndex] == false); + requestedSeg[segmentIndex] = true; DmSegMobileReply result = - await DanmakaHttp.queryDanmaku(cid: cid, segmentIndex: currentSegIndex); + await DanmakaHttp.queryDanmaku(cid: cid, segmentIndex: segmentIndex + 1); if (result.elems.isNotEmpty) { - result.elems.sort((a, b) => (a.progress).compareTo(b.progress)); - // dmSegList.add(result); - currentSegIndex = currentSegIndex < 1 ? 1 : currentSegIndex; - dmSegList[currentSegIndex - 1] = result; + for (var element in result.elems) { + int pos = element.progress ~/ 100;//每0.1秒存储一次 + if (dmSegMap[pos] == null) { + dmSegMap[pos] = []; + } + dmSegMap[pos]!.add(element); + } } - if (dmSegList.isNotEmpty) { - findClosestPositionIndex(playerController.position.value.inMilliseconds); - } - return dmSegList; } - /// 查询当前最接近的弹幕 - void findClosestPositionIndex(int position) { - int segIndex = (position / (6 * 60 * 1000)).ceil() - 1; - if (segIndex < 0) segIndex = 0; - List elems = dmSegList[segIndex].elems; - - if (segIndex < dmSegList.length) { - int left = 0; - int right = elems.length; - - while (left < right) { - int mid = (right + left) ~/ 2; - var midPosition = elems[mid].progress; - - if (midPosition >= position) { - right = mid; - } else { - left = mid + 1; - } - } - - currentSegIndex = segIndex; - currentDmIndex = right; - } else { - currentSegIndex = segIndex; - currentDmIndex = 0; + List? getCurrentDanmaku(int progress) { + int segmentIndex = calcSegment(progress); + if (!requestedSeg[segmentIndex]) { + queryDanmaku(segmentIndex); } + return dmSegMap[progress ~/ 100]; } } diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 1ff7677c..027b9dfa 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:ns_danmaku/ns_danmaku.dart'; +import 'package:pilipala/models/danmaku/dm.pb.dart'; import 'package:pilipala/pages/danmaku/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/utils/danmaku.dart'; @@ -27,7 +28,7 @@ class _PlDanmakuState extends State { late PlPlayerController playerController; late PlDanmakuController _plDanmakuController; DanmakuController? _controller; - bool danmuPlayStatus = true; + // bool danmuPlayStatus = true; Box setting = GStrorage.setting; late bool enableShowDanmaku; late List blockTypes; @@ -35,6 +36,7 @@ class _PlDanmakuState extends State { late double opacityVal; late double fontSizeVal; late double danmakuDurationVal; + int latestAddedPosition = -1; @override void initState() { @@ -42,26 +44,25 @@ class _PlDanmakuState extends State { enableShowDanmaku = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); _plDanmakuController = - PlDanmakuController(widget.cid, widget.playerController); + PlDanmakuController(widget.cid); if (mounted) { playerController = widget.playerController; - _plDanmakuController.videoDuration = playerController.duration.value; if (enableShowDanmaku || playerController.isOpenDanmu.value) { - _plDanmakuController - ..calcSegment() - ..queryDanmaku(); + _plDanmakuController.initiate( + playerController.duration.value.inMilliseconds, + playerController.position.value.inMilliseconds + ); } playerController ..addStatusLister(playerListener) ..addPositionListener(videoPositionListen); } playerController.isOpenDanmu.listen((p0) { - if (p0) { - if (_plDanmakuController.dmSegList.isEmpty) { - _plDanmakuController - ..calcSegment() - ..queryDanmaku(); - } + if (p0 && !_plDanmakuController.initiated) { + _plDanmakuController.initiate( + playerController.duration.value.inMilliseconds, + playerController.position.value.inMilliseconds + ); } }); blockTypes = playerController.blockTypes; @@ -82,68 +83,32 @@ class _PlDanmakuState extends State { } void videoPositionListen(Duration position) { - if (!danmuPlayStatus) { - _controller!.onResume(); - danmuPlayStatus = true; - } if (!playerController.isOpenDanmu.value) { return; } - PlDanmakuController ctr = _plDanmakuController; int currentPosition = position.inMilliseconds; - blockTypes = playerController.blockTypes; - // 根据position判断是否有已缓存弹幕。没有则请求对应段 - int segIndex = (currentPosition / (6 * 60 * 1000)).ceil(); - segIndex = segIndex < 1 ? 1 : segIndex; - // print('🌹🌹: ${segIndex}'); - // print('🌹🌹: ${ctr.dmSegList.length}'); - // print('🌹🌹: ${ctr.hasrequestSeg.contains(segIndex - 1)}'); - if (segIndex - 1 >= ctr.dmSegList.length || - (ctr.dmSegList[segIndex - 1].elems.isEmpty && - !ctr.hasrequestSeg.contains(segIndex - 1))) { - ctr.hasrequestSeg.add(segIndex - 1); - ctr.currentSegIndex = segIndex; - EasyThrottle.throttle('follow', const Duration(seconds: 1), () { - ctr.queryDanmaku(); - }); - } - // 超出分段数返回 - if (ctr.currentSegIndex >= ctr.dmSegList.length) { - return; - } - if (ctr.dmSegList.isEmpty || - ctr.dmSegList[ctr.currentSegIndex].elems.isEmpty) { - return; - } - // 超出当前分段的弹幕总数返回 - if (ctr.currentDmIndex >= ctr.dmSegList[ctr.currentSegIndex].elems.length) { - ctr.currentDmIndex = 0; - ctr.currentSegIndex++; - return; - } - var element = ctr.dmSegList[ctr.currentSegIndex].elems[ctr.currentDmIndex]; - var delta = currentPosition - element.progress; + currentPosition -= currentPosition % 100;//取整百的毫秒数 - if (delta >= 0 && delta < 200) { - // 屏蔽彩色弹幕 - if (blockTypes.contains(6) ? element.color == 16777215 : true) { - _controller!.addItems([ - DanmakuItem( - element.content, - color: DmUtils.decimalToColor(element.color), - time: element.progress, - type: DmUtils.getPosition(element.mode), - ) - ]); - } - ctr.currentDmIndex++; - } else { - if (!playerController.isOpenDanmu.value) { - _controller!.pause(); - danmuPlayStatus = false; - return; - } - ctr.findClosestPositionIndex(position.inMilliseconds); + if (currentPosition == latestAddedPosition) { + return; + } + latestAddedPosition = currentPosition; + + List? currentDanmakuList = + _plDanmakuController.getCurrentDanmaku(currentPosition); + + if (currentDanmakuList != null) { + Color? defaultColor = playerController.blockTypes.contains(6) ? + DmUtils.decimalToColor(16777215) : null; + + _controller!.addItems( + currentDanmakuList.map((e) => DanmakuItem( + e.content, + color: defaultColor ?? DmUtils.decimalToColor(e.color), + time: e.progress, + type: DmUtils.getPosition(e.mode), + )).toList() + ); } } diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 53c9e497..0ca09b8c 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -23,7 +23,14 @@ class ActionPanel extends StatefulWidget { class _ActionPanelState extends State { final DynamicsController _dynamicsController = Get.put(DynamicsController()); late ModuleStatModel stat; - + bool isProcessing = false; + void Function()? handleState(Future Function() action) { + return isProcessing ? null : () async { + setState(() => isProcessing = true); + await action(); + setState(() => isProcessing = false); + }; + } @override void initState() { super.initState(); @@ -31,7 +38,7 @@ class _ActionPanelState extends State { } // 动态点赞 - onLikeDynamic() async { + Future onLikeDynamic() async { feedBack(); var item = widget.item!; String dynamicId = item.idStr!; @@ -101,7 +108,7 @@ class _ActionPanelState extends State { Expanded( flex: 1, child: TextButton.icon( - onPressed: () => onLikeDynamic(), + onPressed: handleState(onLikeDynamic), icon: Icon( stat.like!.status! ? FontAwesomeIcons.solidThumbsUp diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 14ede4d3..70169e3d 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -116,16 +116,28 @@ class MemberController extends GetxController { Future relationSearch() async { if (userInfo == null) return; if (mid == ownerMid) return; - var res = await UserHttp.relationSearch(mid); + var res = await UserHttp.hasFollow(mid); if (res['status']) { - attribute.value = res['data']['relation']['attribute']; - attributeText.value = attribute.value == 0 - ? '关注' - : attribute.value == 2 - ? '已关注' - : attribute.value == 6 - ? '已互粉' - : '已拉黑'; + attribute.value = res['data']['attribute']; + switch (attribute.value) { + case 1: + attributeText.value = '悄悄关注'; + break; + case 2: + attributeText.value = '已关注'; + break; + case 6: + attributeText.value = '已互关'; + break; + case 128: + attributeText.value = '已拉黑'; + break; + default: + attributeText.value = '关注'; + } + if (res['data']['special'] == 1) { + attributeText.value += 'SP'; + } } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 98ce9ba8..a0d4adde 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; @@ -134,7 +136,14 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late int mid; late String memberHeroTag; late bool enableAi; - + bool isProcessing = false; + void Function()? handleState(Future Function() action) { + return isProcessing ? null : () async { + setState(() => isProcessing = true); + await action(); + setState(() => isProcessing = false); + }; + } @override void initState() { super.initState(); @@ -477,7 +486,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { () => ActionItem( icon: const Icon(FontAwesomeIcons.thumbsUp), selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), - onTap: () => videoIntroController.actionLikeVideo(), + onTap: handleState(videoIntroController.actionLikeVideo), selectStatus: videoIntroController.hasLike.value, loadingStatus: loadingStatus, text: !loadingStatus @@ -494,7 +503,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { () => ActionItem( icon: const Icon(FontAwesomeIcons.b), selectIcon: const Icon(FontAwesomeIcons.b), - onTap: () => videoIntroController.actionCoinVideo(), + onTap: handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin.value, loadingStatus: loadingStatus, text: !loadingStatus @@ -538,7 +547,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Obx( () => ActionRowItem( icon: const Icon(FontAwesomeIcons.thumbsUp), - onTap: () => videoIntroController.actionLikeVideo(), + onTap: handleState(videoIntroController.actionLikeVideo), selectStatus: videoIntroController.hasLike.value, loadingStatus: loadingStatus, text: @@ -549,7 +558,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Obx( () => ActionRowItem( icon: const Icon(FontAwesomeIcons.b), - onTap: () => videoIntroController.actionCoinVideo(), + onTap: handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin.value, loadingStatus: loadingStatus, text: diff --git a/lib/pages/video/detail/reply/widgets/zan.dart b/lib/pages/video/detail/reply/widgets/zan.dart index da0c1378..b9ead14e 100644 --- a/lib/pages/video/detail/reply/widgets/zan.dart +++ b/lib/pages/video/detail/reply/widgets/zan.dart @@ -22,9 +22,9 @@ class ZanButton extends StatefulWidget { class _ZanButtonState extends State { // 评论点赞 - onLikeReply() async { + Future onLikeReply() async { feedBack(); - SmartDialog.showLoading(msg: 'pilipala ...'); + // SmartDialog.showLoading(msg: 'pilipala ...'); ReplyItemModel replyItem = widget.replyItem!; int oid = replyItem.oid!; int rpid = replyItem.rpid!; @@ -32,7 +32,7 @@ class _ZanButtonState extends State { int action = replyItem.action == 0 ? 1 : 0; var res = await ReplyHttp.likeReply( type: widget.replyType!.index, oid: oid, rpid: rpid, action: action); - SmartDialog.dismiss(); + // SmartDialog.dismiss(); if (res['status']) { SmartDialog.showToast(replyItem.action == 0 ? '点赞成功 👍' : '取消赞 💔'); if (action == 1) { @@ -47,6 +47,14 @@ class _ZanButtonState extends State { SmartDialog.showToast(res['msg']); } } + bool isProcessing = false; + void Function()? handleState(Future Function() action) { + return isProcessing ? null : () async { + setState(() => isProcessing = true); + await action(); + setState(() => isProcessing = false); + }; + } @override Widget build(BuildContext context) { @@ -55,6 +63,7 @@ class _ZanButtonState extends State { return SizedBox( height: 32, child: TextButton( + onPressed: handleState(onLikeReply), child: Row( children: [ Icon( @@ -79,7 +88,6 @@ class _ZanButtonState extends State { ), ], ), - onPressed: () => onLikeReply(), ), ); } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index f02fcd6d..184a38ec 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -237,17 +237,17 @@ class _VideoDetailPageState extends State final double pinnedHeaderHeight = statusBarHeight + kToolbarHeight + videoHeight; if (MediaQuery.of(context).orientation == Orientation.landscape || - plPlayerController!.isFullScreen.value) { + plPlayerController?.isFullScreen.value == true) { enterFullScreen(); } else { exitFullScreen(); } Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait, - bottom: MediaQuery.of(context).orientation == Orientation.portrait - && plPlayerController!.isFullScreen.value, - left: !plPlayerController!.isFullScreen.value, - right: !plPlayerController!.isFullScreen.value, + bottom: MediaQuery.of(context).orientation == Orientation.portrait && + plPlayerController?.isFullScreen.value == true, + left: plPlayerController?.isFullScreen.value != true, + right: plPlayerController?.isFullScreen.value != true, child: Stack( children: [ Scaffold( @@ -259,169 +259,168 @@ class _VideoDetailPageState extends State headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return [ - Obx(() => PopScope( - canPop: !plPlayerController!.isFullScreen.value, - onPopInvoked: (bool didPop) { - if (plPlayerController!.isFullScreen.value) { - plPlayerController! - .triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape) { - verticalScreen(); - } - }, - child: SliverAppBar( - automaticallyImplyLeading: false, - pinned: false, - elevation: 0, - scrolledUnderElevation: 0, - forceElevated: innerBoxIsScrolled, - expandedHeight: - plPlayerController!.isFullScreen.value || - MediaQuery.of(context).orientation == - Orientation.landscape - ? MediaQuery.of(context).size.height - - (MediaQuery.of(context).orientation == - Orientation.landscape - ? 0 - : statusBarHeight) - : videoHeight, - backgroundColor: Colors.black, - flexibleSpace: FlexibleSpaceBar( - background: LayoutBuilder( - builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: ((context, snapshot) { - if (snapshot.hasData && - snapshot.data['status']) { - return Obx( - () => !videoDetailController - .autoPlay.value - ? const SizedBox() - : PLVideoPlayer( - controller: - plPlayerController!, - headerControl: - videoDetailController - .headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key( - videoDetailController - .danmakuCid - .value - .toString()), - cid: - videoDetailController - .danmakuCid - .value, - playerController: - plPlayerController!, - ), + SliverAppBar( + automaticallyImplyLeading: false, + pinned: false, + elevation: 0, + scrolledUnderElevation: 0, + forceElevated: innerBoxIsScrolled, + expandedHeight: + plPlayerController?.isFullScreen.value == true || + MediaQuery.of(context).orientation == + Orientation.landscape + ? MediaQuery.of(context).size.height - + (MediaQuery.of(context).orientation == + Orientation.landscape + ? 0 + : statusBarHeight) + : videoHeight, + backgroundColor: Colors.black, + flexibleSpace: FlexibleSpaceBar( + background: PopScope( + canPop: + plPlayerController?.isFullScreen.value != true, + onPopInvoked: (bool didPop) { + if (plPlayerController?.isFullScreen.value == + true) { + plPlayerController! + .triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape) { + verticalScreen(); + } + }, + child: LayoutBuilder( + builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + FutureBuilder( + future: _futureBuilderFuture, + builder: ((context, snapshot) { + if (snapshot.hasData && + snapshot.data['status']) { + return Obx( + () => !videoDetailController + .autoPlay.value + ? const SizedBox() + : PLVideoPlayer( + controller: + plPlayerController!, + headerControl: + videoDetailController + .headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key( + videoDetailController + .danmakuCid + .value + .toString()), + cid: + videoDetailController + .danmakuCid + .value, + playerController: + plPlayerController!, ), ), - ); - } else { - return const SizedBox(); - } - }), - ), + ), + ); + } else { + return const SizedBox(); + } + }), + ), - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: maxWidth, - height: maxHeight, - ), + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController + .videoItem['pic'], + width: maxWidth, + height: maxHeight, ), ), ), + ), - /// 关闭自动播放时 手动播放 - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value && - videoDetailController - .isEffective.value && - !videoDetailController - .autoPlay.value, - child: Stack( - children: [ - Positioned( - top: 0, - left: 0, - right: 0, - child: AppBar( - primary: false, - foregroundColor: - Colors.white, + /// 关闭自动播放时 手动播放 + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value && + videoDetailController + .isEffective.value && + !videoDetailController + .autoPlay.value, + child: Stack( + children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: AppBar( + primary: false, + foregroundColor: Colors.white, + backgroundColor: + Colors.transparent, + actions: [ + IconButton( + tooltip: '稍后再看', + onPressed: () async { + var res = await UserHttp + .toViewLater( + bvid: + videoDetailController + .bvid); + SmartDialog.showToast( + res['msg']); + }, + icon: const Icon(Icons + .history_outlined), + ), + const SizedBox(width: 14) + ], + ), + ), + Positioned( + right: 12, + bottom: 10, + child: TextButton.icon( + style: ButtonStyle( backgroundColor: - Colors.transparent, - actions: [ - IconButton( - tooltip: '稍后再看', - onPressed: () async { - var res = await UserHttp - .toViewLater( - bvid: - videoDetailController - .bvid); - SmartDialog.showToast( - res['msg']); - }, - icon: const Icon(Icons - .history_outlined), - ), - const SizedBox(width: 14) - ], + MaterialStateProperty + .resolveWith( + (states) { + return Theme.of(context) + .colorScheme + .primaryContainer; + }), ), - ), - Positioned( - right: 12, - bottom: 10, - child: TextButton.icon( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty - .resolveWith( - (states) { - return Theme.of(context) - .colorScheme - .primaryContainer; - }), - ), - onPressed: () => - handlePlay(), - icon: const Icon( - Icons.play_circle_outline, - size: 20, - ), - label: const Text('Play'), + onPressed: () => handlePlay(), + icon: const Icon( + Icons.play_circle_outline, + size: 20, ), + label: const Text('Play'), ), - ], - )), - ), - ], - ); - }, - ), + ), + ], + )), + ), + ], + ); + }, )), )), ]; @@ -433,8 +432,9 @@ class _VideoDetailPageState extends State // }, /// 不收回 pinnedHeaderSliverHeightBuilder: () { - return plPlayerController!.isFullScreen.value ? - MediaQuery.of(context).size.height: pinnedHeaderHeight; + return plPlayerController?.isFullScreen.value == true + ? MediaQuery.of(context).size.height + : pinnedHeaderHeight; }, onlyOneScrollInBody: true, body: Container(