mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-30 23:58:13 +08:00
mod: merge tabbar from pilipala
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:PiliPalaX/utils/extension.dart';
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -26,7 +27,7 @@ class VideoReplyController extends GetxController {
|
|||||||
String nextOffset = "";
|
String nextOffset = "";
|
||||||
bool isLoadingMore = false;
|
bool isLoadingMore = false;
|
||||||
RxString noMore = ''.obs;
|
RxString noMore = ''.obs;
|
||||||
RxInt count = 0.obs;
|
RxInt count = (-1).obs;
|
||||||
// 当前回复的回复
|
// 当前回复的回复
|
||||||
ReplyItemModel? currentReplyItem;
|
ReplyItemModel? currentReplyItem;
|
||||||
|
|
||||||
@@ -77,7 +78,6 @@ class VideoReplyController extends GetxController {
|
|||||||
if (res['data'].cursor.isEnd == true) {
|
if (res['data'].cursor.isEnd == true) {
|
||||||
noMore.value = '没有更多了';
|
noMore.value = '没有更多了';
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 未登录状态replies可能返回null
|
// 未登录状态replies可能返回null
|
||||||
noMore.value = nextOffset == "" && type == 'init' ? '还没有评论' : '没有更多了';
|
noMore.value = nextOffset == "" && type == 'init' ? '还没有评论' : '没有更多了';
|
||||||
@@ -126,4 +126,8 @@ class VideoReplyController extends GetxController {
|
|||||||
queryReplyList(type: 'init');
|
queryReplyList(type: 'init');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void animToTop() {
|
||||||
|
scrollController.animToTop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,9 +57,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
VideoReplyController(widget.oid, widget.rpid.toString(), replyLevel),
|
VideoReplyController(widget.oid, widget.rpid.toString(), replyLevel),
|
||||||
tag: widget.rpid.toString());
|
tag: widget.rpid.toString());
|
||||||
} else {
|
} else {
|
||||||
_videoReplyController = Get.put(
|
_videoReplyController = Get.find<VideoReplyController>(tag: heroTag);
|
||||||
VideoReplyController(widget.oid, '', replyLevel),
|
|
||||||
tag: heroTag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fabAnimationCtr = AnimationController(
|
fabAnimationCtr = AnimationController(
|
||||||
@@ -135,38 +133,20 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
key: const PageStorageKey<String>('评论'),
|
key: const PageStorageKey<String>('评论'),
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
SliverPersistentHeader(
|
SliverPersistentHeader(
|
||||||
pinned: true,
|
pinned: false,
|
||||||
floating: false,
|
floating: true,
|
||||||
delegate: _MySliverPersistentHeaderDelegate(
|
delegate: _MySliverPersistentHeaderDelegate(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 45,
|
height: 40,
|
||||||
padding: const EdgeInsets.fromLTRB(12, 0, 6, 0),
|
padding: const EdgeInsets.fromLTRB(12, 0, 6, 0),
|
||||||
decoration: BoxDecoration(
|
color: Theme.of(context).colorScheme.surface,
|
||||||
color: Theme.of(context).colorScheme.background,
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline
|
|
||||||
.withOpacity(0.1)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Obx(
|
Obx(
|
||||||
() => AnimatedSwitcher(
|
() => Text(
|
||||||
duration: const Duration(milliseconds: 400),
|
'${_videoReplyController.sortTypeLabel.value}评论',
|
||||||
transitionBuilder:
|
style: const TextStyle(fontSize: 13),
|
||||||
(Widget child, Animation<double> animation) {
|
|
||||||
return ScaleTransition(
|
|
||||||
scale: animation, child: child);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'共${_videoReplyController.count.value}条回复',
|
|
||||||
key: ValueKey<int>(
|
|
||||||
_videoReplyController.count.value),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@@ -175,10 +155,12 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
_videoReplyController.queryBySort(),
|
_videoReplyController.queryBySort(),
|
||||||
icon: const Icon(Icons.sort, size: 16),
|
icon: const Icon(Icons.sort, size: 16),
|
||||||
label: Obx(() => Text(
|
label: Obx(
|
||||||
|
() => Text(
|
||||||
_videoReplyController.sortTypeLabel.value,
|
_videoReplyController.sortTypeLabel.value,
|
||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: 13),
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:PiliPalaX/utils/extension.dart';
|
||||||
import 'package:auto_orientation/auto_orientation.dart';
|
import 'package:auto_orientation/auto_orientation.dart';
|
||||||
import 'package:floating/floating.dart';
|
import 'package:floating/floating.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@@ -40,10 +41,12 @@ class VideoDetailPage extends StatefulWidget {
|
|||||||
class _VideoDetailPageState extends State<VideoDetailPage>
|
class _VideoDetailPageState extends State<VideoDetailPage>
|
||||||
with TickerProviderStateMixin, RouteAware {
|
with TickerProviderStateMixin, RouteAware {
|
||||||
late VideoDetailController videoDetailController;
|
late VideoDetailController videoDetailController;
|
||||||
|
late VideoReplyController _videoReplyController;
|
||||||
PlPlayerController? plPlayerController;
|
PlPlayerController? plPlayerController;
|
||||||
late StreamController<double> appbarStream;
|
late StreamController<double> appbarStream;
|
||||||
late VideoIntroController videoIntroController;
|
late VideoIntroController videoIntroController;
|
||||||
late BangumiIntroController bangumiIntroController;
|
late BangumiIntroController bangumiIntroController;
|
||||||
|
late final _introController = ScrollController();
|
||||||
late String heroTag;
|
late String heroTag;
|
||||||
|
|
||||||
PlayerStatus playerStatus = PlayerStatus.playing;
|
PlayerStatus playerStatus = PlayerStatus.playing;
|
||||||
@@ -77,6 +80,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
heroTag = Get.arguments['heroTag'];
|
heroTag = Get.arguments['heroTag'];
|
||||||
}
|
}
|
||||||
videoDetailController = Get.put(VideoDetailController(), tag: 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 = Get.put(VideoIntroController(), tag: heroTag);
|
||||||
videoIntroController.videoDetail.listen((value) {
|
videoIntroController.videoDetail.listen((value) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
@@ -384,6 +390,104 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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(
|
Widget plPlayer = FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
@@ -587,33 +691,17 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ColoredBox(
|
child: ColoredBox(
|
||||||
key: Key(heroTag),
|
key: Key(heroTag),
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Opacity(
|
tabbarBuild,
|
||||||
// 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(),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
controller: videoDetailController.tabCtr,
|
controller: videoDetailController.tabCtr,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
CustomScrollView(
|
CustomScrollView(
|
||||||
|
controller: _introController,
|
||||||
key: const PageStorageKey<String>('简介'),
|
key: const PageStorageKey<String>('简介'),
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
if (videoDetailController.videoType ==
|
if (videoDetailController.videoType ==
|
||||||
|
|||||||
@@ -5,3 +5,13 @@ extension ImageExtension on num {
|
|||||||
return (this * MediaQuery.of(context).devicePixelRatio).round();
|
return (this * MediaQuery.of(context).devicePixelRatio).round();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ScrollControllerExt on ScrollController {
|
||||||
|
void animToTop() {
|
||||||
|
animateTo(
|
||||||
|
0,
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
curve: Curves.ease,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user