From 54a5fc61f057ece26dbb1cdd31947056f2b2740c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 9 Jul 2023 00:35:28 +0800 Subject: [PATCH] =?UTF-8?q?mod:=20=E6=92=AD=E6=94=BE=E9=A1=B5=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 5 +- lib/pages/video/detail/introduction/view.dart | 152 +++++---- lib/pages/video/detail/reply/view.dart | 1 + lib/pages/video/detail/view.dart | 320 ++++++++++-------- 4 files changed, 256 insertions(+), 222 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index e9fafc8a..87f05ed6 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -10,8 +10,10 @@ import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/pages/video/detail/replyReply/index.dart'; -class VideoDetailController extends GetxController { +class VideoDetailController extends GetxController + with GetSingleTickerProviderStateMixin { int tabInitialIndex = 0; + TabController? tabCtr; // tabs RxList tabs = ['简介', '评论'].obs; @@ -63,6 +65,7 @@ class VideoDetailController extends GetxController { } heroTag = Get.arguments['heroTag']; } + tabCtr = TabController(length: 2, vsync: this); queryVideoUrl(); } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 1a9ca176..b11da53e 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -7,6 +7,7 @@ import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/fav/index.dart'; import 'package:pilipala/pages/favDetail/index.dart'; +import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/widgets/expandable_section.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/stat/danmu.dart'; @@ -99,6 +100,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { final FavController _favController = Get.put(FavController()); + late VideoDetailController? videoDetailCtr; @override void initState() { super.initState(); @@ -110,6 +112,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); _manualAnimation = Tween(begin: 0.5, end: 1.5).animate(_manualController!); + videoDetailCtr = + Get.find(tag: Get.arguments['heroTag']); } showFavBottomSheet() { @@ -345,7 +349,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), const SizedBox(height: 8), // 点赞收藏转发 - _actionGrid(context, videoIntroController), + _actionGrid(context, videoIntroController, videoDetailCtr), // 合集 if (!widget.loadingStatus && widget.videoDetail!.ugcSeason != null) ...[ @@ -425,88 +429,90 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } // 喜欢 投币 分享 - Widget _actionGrid(BuildContext context, videoIntroController) { + Widget _actionGrid( + BuildContext context, videoIntroController, videoDetailCtr) { return LayoutBuilder(builder: (context, constraints) { return SizedBox( height: constraints.maxWidth / 5 * 0.8, - child: GridView.count( - primary: false, - padding: const EdgeInsets.all(0), - crossAxisCount: 5, - childAspectRatio: 1.25, - children: [ - // ActionItem( - // icon: const Icon(FontAwesomeIcons.s), - // selectIcon: const Icon(FontAwesomeIcons.s), - // onTap: () => {}, - // selectStatus: true, - // loadingStatus: false, - // text: '三连', - // ), - // Column( - // children: [], - // ), - InkWell( - onTap: () => videoIntroController.actionOneThree(), - borderRadius: StyleString.mdRadius, - child: Padding( - padding: const EdgeInsets.all(12), - child: Image.asset( - 'assets/images/logo/logo_big.png', - width: 10, - height: 10, - ), + child: Material( + child: GridView.count( + primary: false, + padding: const EdgeInsets.all(0), + crossAxisCount: 5, + childAspectRatio: 1.25, + children: [ + // InkWell( + // onTap: () => videoIntroController.actionOneThree(), + // borderRadius: StyleString.mdRadius, + // child: Padding( + // padding: const EdgeInsets.all(12), + // child: Image.asset( + // 'assets/images/logo/logo_big.png', + // width: 10, + // height: 10, + // ), + // ), + // ), + Obx( + () => ActionItem( + icon: const Icon(FontAwesomeIcons.thumbsUp), + selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), + onTap: () => videoIntroController.actionLikeVideo(), + selectStatus: videoIntroController.hasLike.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.like!.toString() + : '-'), ), - ), - Obx( - () => ActionItem( - icon: const Icon(FontAwesomeIcons.thumbsUp), - selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), - onTap: () => videoIntroController.actionLikeVideo(), - selectStatus: videoIntroController.hasLike.value, + // ActionItem( + // icon: const Icon(FontAwesomeIcons.thumbsDown), + // selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), + // onTap: () => {}, + // selectStatus: false, + // loadingStatus: widget.loadingStatus, + // text: '不喜欢'), + Obx( + () => ActionItem( + icon: const Icon(FontAwesomeIcons.b), + selectIcon: const Icon(FontAwesomeIcons.b), + onTap: () => videoIntroController.actionCoinVideo(), + selectStatus: videoIntroController.hasCoin.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.coin!.toString() + : '-'), + ), + Obx( + () => ActionItem( + icon: const Icon(FontAwesomeIcons.heart), + selectIcon: const Icon(FontAwesomeIcons.heartCircleCheck), + onTap: () => showFavBottomSheet(), + selectStatus: videoIntroController.hasFav.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.favorite!.toString() + : '-'), + ), + ActionItem( + icon: const Icon(FontAwesomeIcons.shareFromSquare), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, loadingStatus: widget.loadingStatus, text: !widget.loadingStatus - ? widget.videoDetail!.stat!.like!.toString() + ? widget.videoDetail!.stat!.share!.toString() : '-'), - ), - // ActionItem( - // icon: const Icon(FontAwesomeIcons.thumbsDown), - // selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), - // onTap: () => {}, - // selectStatus: false, - // loadingStatus: widget.loadingStatus, - // text: '不喜欢'), - Obx( - () => ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), - onTap: () => videoIntroController.actionCoinVideo(), - selectStatus: videoIntroController.hasCoin.value, + ActionItem( + icon: const Icon(FontAwesomeIcons.comments), + onTap: () { + videoDetailCtr.tabCtr.animateTo(1); + }, + selectStatus: false, loadingStatus: widget.loadingStatus, text: !widget.loadingStatus - ? widget.videoDetail!.stat!.coin!.toString() + ? widget.videoDetail!.stat!.reply!.toString() : '-'), - ), - Obx( - () => ActionItem( - icon: const Icon(FontAwesomeIcons.heart), - selectIcon: const Icon(FontAwesomeIcons.heartCircleCheck), - onTap: () => showFavBottomSheet(), - selectStatus: videoIntroController.hasFav.value, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.videoDetail!.stat!.favorite!.toString() - : '-'), - ), - ActionItem( - icon: const Icon(FontAwesomeIcons.shareFromSquare), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.videoDetail!.stat!.share!.toString() - : '-'), - ], + ], + ), ), ); }); diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 3549a66c..16489ef2 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -136,6 +136,7 @@ class _VideoReplyPanelState extends State controller: _videoReplyController.scrollController, key: const PageStorageKey('评论'), slivers: [ + const SliverToBoxAdapter(child: SizedBox(height: 12)), FutureBuilder( future: _futureBuilderFuture, builder: (context, snapshot) { diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 30eb59f2..cd9a9277 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:ui'; import 'package:extended_image/extended_image.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; @@ -144,120 +145,139 @@ class _VideoDetailPageState extends State final videoHeight = MediaQuery.of(context).size.width * 9 / 16; final double pinnedHeaderHeight = statusBarHeight + kToolbarHeight + videoHeight; - return DefaultTabController( - initialIndex: videoDetailController.tabInitialIndex, - length: videoDetailController.tabs.length, // tab的数量. - child: SafeArea( - top: false, - bottom: false, - child: Stack( - children: [ - Scaffold( - resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, - body: ExtendedNestedScrollView( - controller: _extendNestCtr, - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverAppBar( - automaticallyImplyLeading: false, - pinned: false, - elevation: 0, - scrolledUnderElevation: 0, - forceElevated: innerBoxIsScrolled, - expandedHeight: videoHeight, - // collapsedHeight: videoHeight, - backgroundColor: Theme.of(context).colorScheme.background, - flexibleSpace: FlexibleSpaceBar( - background: Padding( - padding: EdgeInsets.only( - top: MediaQuery.of(context).padding.top), - child: LayoutBuilder( - builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - // double PR = - // MediaQuery.of(context).devicePixelRatio; - return Hero( - tag: videoDetailController.heroTag, - child: Stack( - children: [ - AspectRatio( - aspectRatio: 16 / 9, - child: MeeduVideoPlayer( - controller: _meeduPlayerController!, - header: (BuildContext context, - MeeduPlayerController - _meeduPlayerController, - Responsive) { - return AppBar( - toolbarHeight: 40, - backgroundColor: Colors.transparent, - primary: false, - elevation: 0, - scrolledUnderElevation: 0, - foregroundColor: Colors.white, - leading: IconButton( - onPressed: () { - Get.back(); - }, - icon: const Icon( - Icons.arrow_back_ios, - size: 19, - ), + return SafeArea( + top: false, + bottom: false, + child: Stack( + children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController.videoItem['pic'], + width: Get.size.width, + height: videoHeight + 100, + ), + ), + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 100, sigmaY: 100), //可以看源码 + child: Container( + decoration: BoxDecoration( + color: + Theme.of(context).colorScheme.background.withOpacity(0.1), + ), + ), + ), + ), + Scaffold( + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + backgroundColor: Colors.transparent, + body: ExtendedNestedScrollView( + controller: _extendNestCtr, + headerSliverBuilder: + (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverAppBar( + automaticallyImplyLeading: false, + pinned: false, + elevation: 0, + scrolledUnderElevation: 0, + forceElevated: innerBoxIsScrolled, + expandedHeight: videoHeight, + backgroundColor: Colors.transparent, + // backgroundColor: Theme.of(context).colorScheme.background, + flexibleSpace: FlexibleSpaceBar( + background: Padding( + padding: EdgeInsets.only( + top: MediaQuery.of(context).padding.top), + child: LayoutBuilder( + builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Hero( + tag: videoDetailController.heroTag, + child: Stack( + children: [ + AspectRatio( + aspectRatio: 16 / 9, + child: MeeduVideoPlayer( + controller: _meeduPlayerController!, + header: (BuildContext context, + MeeduPlayerController + _meeduPlayerController, + Responsive) { + return AppBar( + toolbarHeight: 40, + backgroundColor: Colors.transparent, + primary: false, + elevation: 0, + scrolledUnderElevation: 0, + foregroundColor: Colors.white, + leading: IconButton( + onPressed: () { + Get.back(); + }, + icon: const Icon( + Icons.arrow_back_ios, + size: 19, ), - title: Text( - '视频详情', - style: TextStyle( - color: Colors.white, - fontSize: Theme.of(context) - .textTheme - .titleSmall! - .fontSize), - ), - ); - }, + ), + title: Text( + '视频详情', + style: TextStyle( + color: Colors.white, + fontSize: Theme.of(context) + .textTheme + .titleSmall! + .fontSize), + ), + ); + }, + ), + ), + Visibility( + visible: isShowCover, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController + .videoItem['pic'], + width: maxWidth, + height: maxHeight, ), ), - Visibility( - visible: isShowCover, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: maxWidth, - height: maxHeight, - ), - ), - ), - ], - ), - ); - }, - ), + ), + ], + ), + ); + }, ), ), ), - ]; - }, - pinnedHeaderSliverHeightBuilder: () { - return playerStatus != PlayerStatus.playing - ? MediaQuery.of(context).padding.top + 50 - : pinnedHeaderHeight; - }, - onlyOneScrollInBody: true, - body: Column( + ), + ]; + }, + pinnedHeaderSliverHeightBuilder: () { + return playerStatus != PlayerStatus.playing + ? statusBarHeight + kToolbarHeight + : pinnedHeaderHeight; + }, + onlyOneScrollInBody: true, + body: Container( + color: Theme.of(context).colorScheme.background, + child: Column( children: [ Container( width: double.infinity, - height: 45, + height: 0, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, border: Border( bottom: BorderSide( color: @@ -274,7 +294,10 @@ class _VideoDetailPageState extends State margin: const EdgeInsets.only(left: 20), child: Obx( () => TabBar( + controller: videoDetailController.tabCtr, dividerColor: Colors.transparent, + indicatorColor: + Theme.of(context).colorScheme.background, tabs: videoDetailController.tabs .map((String name) => Tab(text: name)) .toList(), @@ -294,6 +317,7 @@ class _VideoDetailPageState extends State ), Expanded( child: TabBarView( + controller: videoDetailController.tabCtr, children: [ Builder( builder: (context) { @@ -314,55 +338,55 @@ class _VideoDetailPageState extends State ), ), ), - // 播放完成/暂停播放 - Positioned( - top: -MediaQuery.of(context).padding.top + - (doubleOffset / videoHeight) * 50, - left: 0, - right: 0, - child: Opacity( - opacity: doubleOffset / videoHeight, - child: Container( - height: 50 + MediaQuery.of(context).padding.top, - color: Theme.of(context).colorScheme.background, - padding: - EdgeInsets.only(top: MediaQuery.of(context).padding.top), - child: AppBar( - primary: false, - elevation: 0, - scrolledUnderElevation: 0, - centerTitle: true, - title: TextButton( - onPressed: () => continuePlay(), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.play_arrow_rounded), - Text( - playerStatus == PlayerStatus.paused - ? '继续播放' - : playerStatus == PlayerStatus.completed - ? '重新播放' - : '播放中', - ) - ], - ), + ), + // 播放完成/暂停播放 + Positioned( + top: -statusBarHeight + + (doubleOffset / (videoHeight - kToolbarHeight)) * + (kToolbarHeight - 9), + left: 0, + right: 0, + child: Opacity( + opacity: doubleOffset / (videoHeight - kToolbarHeight), + child: Container( + height: statusBarHeight + kToolbarHeight, + color: Theme.of(context).colorScheme.background, + padding: EdgeInsets.only(top: statusBarHeight), + child: AppBar( + primary: false, + elevation: 0, + scrolledUnderElevation: 0, + centerTitle: true, + title: TextButton( + onPressed: () => continuePlay(), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.play_arrow_rounded), + Text( + playerStatus == PlayerStatus.paused + ? '继续播放' + : playerStatus == PlayerStatus.completed + ? '重新播放' + : '播放中', + ) + ], ), - actions: [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.share, - size: 20, - )), - const SizedBox(width: 12) - ], ), + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.share, + size: 20, + )), + const SizedBox(width: 12) + ], ), ), ), - ], - ), + ), + ], ), ); }