From 50c911a6a6e17b59dc4c2b0b927ff564c5bd115d Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Mon, 26 Aug 2024 20:40:41 +0800 Subject: [PATCH] mod: merge tabbar from pilipala --- lib/pages/video/detail/reply/controller.dart | 8 +- lib/pages/video/detail/reply/view.dart | 46 +++---- lib/pages/video/detail/view.dart | 126 ++++++++++++++++--- lib/utils/extension.dart | 10 ++ 4 files changed, 137 insertions(+), 53 deletions(-) diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 8c643d87..8bcffe83 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -1,3 +1,4 @@ +import 'package:PiliPalaX/utils/extension.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -26,7 +27,7 @@ class VideoReplyController extends GetxController { String nextOffset = ""; bool isLoadingMore = false; RxString noMore = ''.obs; - RxInt count = 0.obs; + RxInt count = (-1).obs; // 当前回复的回复 ReplyItemModel? currentReplyItem; @@ -77,7 +78,6 @@ class VideoReplyController extends GetxController { if (res['data'].cursor.isEnd == true) { noMore.value = '没有更多了'; } - } else { // 未登录状态replies可能返回null noMore.value = nextOffset == "" && type == 'init' ? '还没有评论' : '没有更多了'; @@ -126,4 +126,8 @@ class VideoReplyController extends GetxController { queryReplyList(type: 'init'); }); } + + void animToTop() { + scrollController.animToTop(); + } } diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index bfa5bd31..c2176ac1 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -57,9 +57,7 @@ class _VideoReplyPanelState extends State VideoReplyController(widget.oid, widget.rpid.toString(), replyLevel), tag: widget.rpid.toString()); } else { - _videoReplyController = Get.put( - VideoReplyController(widget.oid, '', replyLevel), - tag: heroTag); + _videoReplyController = Get.find(tag: heroTag); } fabAnimationCtr = AnimationController( @@ -135,38 +133,20 @@ class _VideoReplyPanelState extends State key: const PageStorageKey('评论'), slivers: [ SliverPersistentHeader( - pinned: true, - floating: false, + pinned: false, + floating: true, delegate: _MySliverPersistentHeaderDelegate( child: Container( - height: 45, + height: 40, padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - border: Border( - bottom: BorderSide( - color: Theme.of(context) - .colorScheme - .outline - .withOpacity(0.1)), - ), - ), + color: Theme.of(context).colorScheme.surface, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Obx( - () => AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - '共${_videoReplyController.count.value}条回复', - key: ValueKey( - _videoReplyController.count.value), - ), + () => Text( + '${_videoReplyController.sortTypeLabel.value}评论', + style: const TextStyle(fontSize: 13), ), ), SizedBox( @@ -175,10 +155,12 @@ class _VideoReplyPanelState extends State onPressed: () => _videoReplyController.queryBySort(), icon: const Icon(Icons.sort, size: 16), - label: Obx(() => Text( - _videoReplyController.sortTypeLabel.value, - style: const TextStyle(fontSize: 13), - )), + label: Obx( + () => Text( + _videoReplyController.sortTypeLabel.value, + style: const TextStyle(fontSize: 13), + ), + ), ), ) ], diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 878f95d7..0c0963cb 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'dart:math'; import 'dart:ui'; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:auto_orientation/auto_orientation.dart'; import 'package:floating/floating.dart'; import 'package:flutter/services.dart'; @@ -40,10 +41,12 @@ class VideoDetailPage extends StatefulWidget { class _VideoDetailPageState extends State with TickerProviderStateMixin, RouteAware { late VideoDetailController videoDetailController; + late VideoReplyController _videoReplyController; PlPlayerController? plPlayerController; late StreamController appbarStream; late VideoIntroController videoIntroController; late BangumiIntroController bangumiIntroController; + late final _introController = ScrollController(); late String heroTag; PlayerStatus playerStatus = PlayerStatus.playing; @@ -77,6 +80,9 @@ class _VideoDetailPageState extends State heroTag = Get.arguments['heroTag']; } videoDetailController = Get.put(VideoDetailController(), tag: heroTag); + _videoReplyController = Get.put( + VideoReplyController(videoDetailController.oid.value, '0', '1'), + tag: heroTag); videoIntroController = Get.put(VideoIntroController(), tag: heroTag); videoIntroController.videoDetail.listen((value) { if (!context.mounted) return; @@ -384,6 +390,104 @@ class _VideoDetailPageState extends State @override Widget build(BuildContext context) { + /// tabbar + Widget tabbarBuild = Container( + width: double.infinity, + height: 45, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + ), + ), + child: Material( + child: Row( + children: [ + Flexible( + flex: 1, + child: Obx( + () => TabBar( + padding: EdgeInsets.zero, + controller: videoDetailController.tabCtr, + labelStyle: const TextStyle(fontSize: 13), + labelPadding: + const EdgeInsets.symmetric(horizontal: 10.0), // 设置每个标签的宽度 + dividerColor: Colors.transparent, + onTap: (value) { + if (!videoDetailController.tabCtr.indexIsChanging) { + if (value == 0) { + _introController.animToTop(); + } else { + _videoReplyController.animToTop(); + } + } + }, + tabs: [ + const Tab(text: '简介'), + Tab( + text: + '评论${_videoReplyController.count.value == -1 ? '' : ' ${_videoReplyController.count.value}'}', + ), + ], + ), + ), + ), + Flexible( + flex: 1, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: 32, + child: TextButton( + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: null, + // onPressed: () => videoDetailController.showShootDanmakuSheet(), + child: + const Text('发弹幕', style: TextStyle(fontSize: 12)), + ), + ), + // SizedBox( + // width: 38, + // height: 38, + // child: Obx( + // () => IconButton( + // onPressed: () { + // plPlayerController?.isOpenDanmu.value = + // !(plPlayerController?.isOpenDanmu.value ?? + // false); + // }, + // icon: !(plPlayerController?.isOpenDanmu.value ?? + // false) + // ? SvgPicture.asset( + // 'assets/images/video/danmu_close.svg', + // // ignore: deprecated_member_use + // color: + // Theme.of(context).colorScheme.outline, + // ) + // : SvgPicture.asset( + // 'assets/images/video/danmu_open.svg', + // // ignore: deprecated_member_use + // color: + // Theme.of(context).colorScheme.primary, + // ), + // ), + // ), + // ), + const SizedBox(width: 14), + ], + ), + )), + ], + ), + ), + ); + Widget plPlayer = FutureBuilder( future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { @@ -587,33 +691,17 @@ class _VideoDetailPageState extends State Expanded( child: ColoredBox( key: Key(heroTag), - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ - // Opacity( - // opacity: 0, - // child: SizedBox( - // width: context.width, - // height: 0, - // 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(), - // ), - // ), - // ), - // ), + tabbarBuild, Expanded( child: TabBarView( physics: const BouncingScrollPhysics(), controller: videoDetailController.tabCtr, children: [ CustomScrollView( + controller: _introController, key: const PageStorageKey('简介'), slivers: [ if (videoDetailController.videoType == diff --git a/lib/utils/extension.dart b/lib/utils/extension.dart index 1b54c628..7d292622 100644 --- a/lib/utils/extension.dart +++ b/lib/utils/extension.dart @@ -5,3 +5,13 @@ extension ImageExtension on num { return (this * MediaQuery.of(context).devicePixelRatio).round(); } } + +extension ScrollControllerExt on ScrollController { + void animToTop() { + animateTo( + 0, + duration: const Duration(milliseconds: 500), + curve: Curves.ease, + ); + } +}