diff --git a/lib/common/widgets/sliver_header.dart b/lib/common/widgets/sliver_header.dart new file mode 100644 index 00000000..13877a53 --- /dev/null +++ b/lib/common/widgets/sliver_header.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class SliverHeaderDelegate extends SliverPersistentHeaderDelegate { + SliverHeaderDelegate({required this.height, required this.child}); + + final double height; + final Widget child; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return child; + } + + @override + double get maxExtent => height; + + @override + double get minExtent => height; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => + true; +} diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 2d500c14..eb3b462d 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,15 +1,11 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; -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'; import 'package:pilipala/common/widgets/stat/view.dart'; @@ -18,6 +14,9 @@ import 'package:pilipala/pages/video/detail/introduction/controller.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; +import 'widgets/action_row_item.dart'; +import 'widgets/intro_detail.dart'; +import 'widgets/menu_row.dart'; import 'widgets/season.dart'; class VideoIntroPanel extends StatefulWidget { @@ -231,135 +230,99 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { return SliverPadding( - padding: const EdgeInsets.only(left: 12, right: 12, top: 10), + padding: const EdgeInsets.only(left: 12, right: 12, top: 15), sliver: SliverToBoxAdapter( child: !widget.loadingStatus || videoItem.isNotEmpty ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SelectableRegion( - magnifierConfiguration: const TextMagnifierConfiguration(), - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Text( - !widget.loadingStatus - ? widget.videoDetail!.title - : videoItem['title'], - style: Theme.of(context).textTheme.titleMedium!.copyWith( - letterSpacing: 0.5, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - InkWell( - splashColor: Colors.transparent, - hoverColor: Colors.transparent, - highlightColor: Colors.transparent, - onTap: () { - _manualController!.animateTo(isExpand ? 0 : 0.5); - setState(() { - isExpand = !isExpand; - }); - }, - child: Row( - children: [ - const SizedBox(width: 2), - StatView( - theme: 'gray', - view: !widget.loadingStatus - ? widget.videoDetail!.stat!.view - : videoItem['stat'].view, - size: 'medium', - ), - const SizedBox(width: 10), - StatDanMu( - theme: 'gray', - danmu: !widget.loadingStatus - ? widget.videoDetail!.stat!.danmaku - : videoItem['stat'].danmaku, - size: 'medium', - ), - const SizedBox(width: 10), - Text( - Utils.dateFormat( - !widget.loadingStatus - ? widget.videoDetail!.pubdate - : videoItem['pubdate'], - formatType: 'detail'), - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.outline), - ), - const Spacer(), - RotationTransition( - turns: _manualAnimation!, - child: SizedBox( - width: 35, - height: 35, - child: IconButton( - padding: const EdgeInsets.all(2.0), - onPressed: () { - /// 0.5代表 180弧度 - _manualController! - .animateTo(isExpand ? 0 : 0.5); - setState(() { - isExpand = !isExpand; - }); - }, - icon: Icon( - FontAwesomeIcons.angleUp, - size: 15, - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - ), - const SizedBox(width: 10), - ], - ), - ), - // 简介 默认收起 - if (!widget.loadingStatus) - ExpandedSection( - expand: isExpand, - begin: 0.0, - end: 1.0, - child: DefaultTextStyle( - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - height: 1.5, - fontSize: - Theme.of(context).textTheme.labelMedium?.fontSize, - ), - child: Padding( - padding: const EdgeInsets.only(bottom: 10), - child: SelectableRegion( - magnifierConfiguration: - const TextMagnifierConfiguration(), - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(widget.videoDetail!.bvid!), - Text.rich( - TextSpan( - children: [ - buildContent( - context, widget.videoDetail!), - ], - ), - ), - ], - ), - ), + Row( + children: [ + Expanded( + child: Text( + !widget.loadingStatus + ? widget.videoDetail!.title + : videoItem['title'], + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + letterSpacing: 0.5, + fontWeight: FontWeight.bold), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), ), - ), - const SizedBox(height: 8), + const SizedBox(width: 20), + SizedBox( + width: 34, + height: 34, + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: + MaterialStateProperty.resolveWith((states) { + return Theme.of(context) + .highlightColor + .withOpacity(0.2); + }), + ), + onPressed: () { + showBottomSheet( + context: context, + enableDrag: true, + builder: (BuildContext context) { + return IntroDetail( + videoDetail: widget.videoDetail!); + }); + }, + icon: const Icon(Icons.more_horiz), + ), + ), + ], + ), + const SizedBox(height: 6), + Row( + children: [ + const SizedBox(width: 2), + StatView( + theme: 'black', + view: !widget.loadingStatus + ? widget.videoDetail!.stat!.view + : videoItem['stat'].view, + size: 'medium', + ), + const SizedBox(width: 10), + StatDanMu( + theme: 'black', + danmu: !widget.loadingStatus + ? widget.videoDetail!.stat!.danmaku + : videoItem['stat'].danmaku, + size: 'medium', + ), + const SizedBox(width: 10), + Text( + Utils.dateFormat( + !widget.loadingStatus + ? widget.videoDetail!.pubdate + : videoItem['pubdate'], + formatType: 'detail'), + style: const TextStyle(fontSize: 12), + ), + ], + ), // 点赞收藏转发 - _actionGrid(context, videoIntroController, videoDetailCtr), + Padding( + padding: const EdgeInsets.only(top: 20), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: actionRow( + context, + videoIntroController, + videoDetailCtr, + ), + ), + ), // 合集 if (!widget.loadingStatus && widget.videoDetail!.ugcSeason != null) ...[ @@ -390,12 +353,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { src: !widget.loadingStatus ? widget.videoDetail!.owner!.face : videoItem['owner'].face, - width: 38, - height: 38, + width: 34, + height: 34, fadeInDuration: Duration.zero, fadeOutDuration: Duration.zero, ), - const SizedBox(width: 14), + const SizedBox(width: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -421,7 +384,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { opacity: widget.loadingStatus ? 0 : 1, duration: const Duration(milliseconds: 150), child: SizedBox( - height: 36, + height: 34, child: Obx( () => videoIntroController.followStatus.isNotEmpty ? ElevatedButton( @@ -444,107 +407,83 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ], ), ), - + const SizedBox(height: 8), Divider( - height: 26, + height: 12, color: Theme.of(context).dividerColor.withOpacity(0.1), ), - // const SizedBox(height: 10), ], ) - : const Center(child: CircularProgressIndicator()), + : const SizedBox( + height: 100, + child: Center( + child: CircularProgressIndicator(), + ), + ), ), ); } - // 喜欢 投币 分享 - Widget _actionGrid( - BuildContext context, videoIntroController, videoDetailCtr) { - return LayoutBuilder(builder: (context, constraints) { - return SizedBox( - height: constraints.maxWidth / 5 * 0.8, - 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() - : '-'), - ), - // 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!.share!.toString() - : '-'), - ActionItem( - icon: const Icon(FontAwesomeIcons.comments), - onTap: () { - videoDetailCtr.tabCtr.animateTo(1); - }, - selectStatus: false, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.videoDetail!.stat!.reply!.toString() - : '-'), - ], - ), + Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) { + return Row(children: [ + Obx( + () => ActionRowItem( + icon: const Icon(FontAwesomeIcons.thumbsUp), + onTap: () => videoIntroController.actionLikeVideo(), + selectStatus: videoIntroController.hasLike.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.like!.toString() + : '-', ), - ); - }); + ), + const SizedBox(width: 8), + Obx( + () => ActionRowItem( + icon: const Icon(FontAwesomeIcons.b), + onTap: () => videoIntroController.actionCoinVideo(), + selectStatus: videoIntroController.hasCoin.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.coin!.toString() + : '-', + ), + ), + const SizedBox(width: 8), + Obx( + () => ActionRowItem( + icon: const Icon(FontAwesomeIcons.heart), + onTap: () => showFavBottomSheet(), + selectStatus: videoIntroController.hasFav.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.favorite!.toString() + : '-', + ), + ), + const SizedBox(width: 8), + ActionRowItem( + icon: const Icon(FontAwesomeIcons.comment), + onTap: () { + videoDetailCtr.tabCtr.animateTo(1); + }, + selectStatus: false, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.reply!.toString() + : '-', + ), + const SizedBox(width: 8), + ActionRowItem( + icon: const Icon(FontAwesomeIcons.share), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.share!.toString() + : '-', + ), + ]); } InlineSpan buildContent(BuildContext context, content) { @@ -583,54 +522,3 @@ class _VideoInfoState extends State with TickerProviderStateMixin { return TextSpan(children: spanChilds); } } - -class ActionItem extends StatelessWidget { - Icon? icon; - Icon? selectIcon; - Function? onTap; - bool? loadingStatus; - String? text; - bool selectStatus = false; - - ActionItem({ - Key? key, - this.icon, - this.selectIcon, - this.onTap, - this.loadingStatus, - this.text, - required this.selectStatus, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () => onTap!(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 4), - selectStatus - ? Icon(selectIcon!.icon!, - size: 21, color: Theme.of(context).primaryColor) - : Icon(icon!.icon!, - size: 21, color: Theme.of(context).colorScheme.outline), - const SizedBox(height: 4), - AnimatedOpacity( - opacity: loadingStatus! ? 0 : 1, - duration: const Duration(milliseconds: 200), - child: Text( - text ?? '', - style: TextStyle( - color: selectStatus - ? Theme.of(context).primaryColor - : Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall?.fontSize), - ), - ), - ], - ), - ); - } -} diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart new file mode 100644 index 00000000..ed06c042 --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:pilipala/common/constants.dart'; + +class ActionItem extends StatelessWidget { + Icon? icon; + Icon? selectIcon; + Function? onTap; + bool? loadingStatus; + String? text; + bool selectStatus = false; + + ActionItem({ + Key? key, + this.icon, + this.selectIcon, + this.onTap, + this.loadingStatus, + this.text, + required this.selectStatus, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () => onTap!(), + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + selectStatus + ? Icon(selectIcon!.icon!, + size: 21, color: Theme.of(context).primaryColor) + : Icon(icon!.icon!, + size: 21, color: Theme.of(context).colorScheme.outline), + const SizedBox(height: 4), + AnimatedOpacity( + opacity: loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: Text( + text ?? '', + style: TextStyle( + color: selectStatus + ? Theme.of(context).primaryColor + : Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context).textTheme.labelSmall?.fontSize), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/video/detail/introduction/widgets/action_row_item.dart b/lib/pages/video/detail/introduction/widgets/action_row_item.dart new file mode 100644 index 00000000..7e447fa3 --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/action_row_item.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:pilipala/common/constants.dart'; + +class ActionRowItem extends StatelessWidget { + Icon? icon; + Icon? selectIcon; + Function? onTap; + bool? loadingStatus; + String? text; + bool selectStatus = false; + + ActionRowItem({ + Key? key, + this.icon, + this.selectIcon, + this.onTap, + this.loadingStatus, + this.text, + required this.selectStatus, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + color: selectStatus + ? Theme.of(context).colorScheme.primaryContainer.withOpacity(0.6) + : Theme.of(context).highlightColor.withOpacity(0.2), + borderRadius: const BorderRadius.all(Radius.circular(30)), + clipBehavior: Clip.hardEdge, + child: InkWell( + onTap: () => onTap!(), + child: Padding( + padding: const EdgeInsets.fromLTRB(13, 6.5, 15, 6.3), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (icon != null) ...[ + Icon(icon!.icon!, + size: 13, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSecondaryContainer), + const SizedBox(width: 6), + ], + AnimatedOpacity( + opacity: loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: Text( + text ?? '', + style: TextStyle( + color: selectStatus + ? Theme.of(context).colorScheme.primary + : null, + fontSize: + Theme.of(context).textTheme.labelMedium?.fontSize), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart new file mode 100644 index 00000000..ab3625a5 --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -0,0 +1,140 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/stat/danmu.dart'; +import 'package:pilipala/common/widgets/stat/view.dart'; +import 'package:pilipala/utils/utils.dart'; + +class IntroDetail extends StatelessWidget { + var videoDetail; + + IntroDetail({ + Key? key, + this.videoDetail, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + color: Theme.of(context).colorScheme.background, + padding: const EdgeInsets.only(left: 14, right: 14), + height: 570, + child: Column( + children: [ + Container( + height: 25, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 40, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer + .withOpacity(0.5), + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoDetail!.title, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + letterSpacing: 0.5, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10), + Row( + children: [ + const SizedBox(width: 2), + StatView( + theme: 'black', + view: videoDetail!.stat!.view, + size: 'medium', + ), + const SizedBox(width: 10), + StatDanMu( + theme: 'black', + danmu: videoDetail!.stat!.danmaku, + size: 'medium', + ), + const SizedBox(width: 10), + Text( + Utils.dateFormat(videoDetail!.pubdate, + formatType: 'detail'), + style: const TextStyle(fontSize: 12), + ), + ], + ), + const SizedBox(height: 20), + SizedBox( + width: double.infinity, + child: SelectableRegion( + magnifierConfiguration: + const TextMagnifierConfiguration(), + focusNode: FocusNode(), + selectionControls: MaterialTextSelectionControls(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(videoDetail!.bvid!), + const SizedBox(height: 4), + Text.rich( + TextSpan( + children: [ + buildContent(context, videoDetail!), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ) + ], + )); + } + + InlineSpan buildContent(BuildContext context, content) { + String desc = content.desc; + List descV2 = content.descV2; + // type + // 1 普通文本 + // 2 @用户 + List spanChilds = []; + if (descV2.isNotEmpty) { + for (var i = 0; i < descV2.length; i++) { + if (descV2[i].type == 1) { + spanChilds.add(TextSpan(text: descV2[i].rawText)); + } else if (descV2[i].type == 2) { + spanChilds.add( + TextSpan( + text: '@${descV2[i].rawText}', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + String heroTag = Utils.makeHeroTag(descV2[i].bizId); + Get.toNamed( + '/member?mid=${descV2[i].bizId}', + arguments: {'face': '', 'heroTag': heroTag}, + ); + }, + ), + ); + } + } + } else { + spanChilds.add(TextSpan(text: desc)); + } + return TextSpan(children: spanChilds); + } +} diff --git a/lib/pages/video/detail/introduction/widgets/menu_row.dart b/lib/pages/video/detail/introduction/widgets/menu_row.dart new file mode 100644 index 00000000..3a644500 --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/menu_row.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; + +class MenuRow extends StatelessWidget { + bool? loadingStatus; + MenuRow({ + Key? key, + this.loadingStatus, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + color: Theme.of(context).colorScheme.background, + padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row(children: [ + actionRowLineItem( + context, + () => {}, + loadingStatus, + '推荐', + selectStatus: true, + ), + const SizedBox(width: 8), + actionRowLineItem( + context, + () => {}, + loadingStatus, + '弹幕', + selectStatus: false, + ), + const SizedBox(width: 8), + actionRowLineItem( + context, + () => {}, + loadingStatus, + '评论列表', + selectStatus: false, + ), + const SizedBox(width: 8), + actionRowLineItem( + context, + () => {}, + loadingStatus, + '播放列表', + selectStatus: false, + ), + ]), + ), + ); + } + + Widget actionRowLineItem( + context, Function? onTap, bool? loadingStatus, String? text, + {bool selectStatus = false}) { + return Material( + color: selectStatus + ? Theme.of(context).highlightColor.withOpacity(0.2) + : Colors.transparent, + borderRadius: const BorderRadius.all(Radius.circular(30)), + clipBehavior: Clip.hardEdge, + child: InkWell( + onTap: () => onTap!(), + child: Container( + padding: const EdgeInsets.fromLTRB(13, 5.5, 13, 5.5), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(30)), + border: Border.all( + color: selectStatus + ? Colors.transparent + : Theme.of(context).highlightColor.withOpacity(0.2), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + AnimatedOpacity( + opacity: loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: Text( + text!, + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium?.fontSize), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index ec204b3a..5afb9dc2 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -5,6 +5,8 @@ import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/common/widgets/sliver_header.dart'; +import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; @@ -234,7 +236,7 @@ class _VideoDetailPageState extends State children: [ Container( width: double.infinity, - height: 45, + height: 0, decoration: BoxDecoration( border: Border( bottom: BorderSide( @@ -254,8 +256,8 @@ class _VideoDetailPageState extends State () => TabBar( controller: videoDetailController.tabCtr, dividerColor: Colors.transparent, - // indicatorColor: - // Theme.of(context).colorScheme.background, + indicatorColor: + Theme.of(context).colorScheme.background, tabs: videoDetailController.tabs .map((String name) => Tab(text: name)) .toList(), @@ -271,11 +273,19 @@ class _VideoDetailPageState extends State children: [ Builder( builder: (context) { - return const CustomScrollView( - key: PageStorageKey('简介'), + return CustomScrollView( + key: const PageStorageKey('简介'), slivers: [ - VideoIntroPanel(), - RelatedVideoPanel(), + const VideoIntroPanel(), + SliverPersistentHeader( + floating: true, + pinned: true, + delegate: SliverHeaderDelegate( + height: 50, + child: MenuRow(loadingStatus: false), + ), + ), + const RelatedVideoPanel(), ], ); },