mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt video/intro page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -20,7 +20,6 @@ import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:canvas_danmaku/canvas_danmaku.dart';
|
||||
@@ -559,7 +558,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
onPressed: () {
|
||||
final newVal = !enableShowDanmaku;
|
||||
plPlayerController.enableShowDanmaku.value = newVal;
|
||||
if (!Pref.tempPlayerConf) {
|
||||
if (!plPlayerController.tempPlayerConf) {
|
||||
GStorage.setting.put(
|
||||
SettingBoxKey.enableShowDanmaku,
|
||||
newVal,
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -89,7 +88,7 @@ class BottomControl extends StatelessWidget {
|
||||
onPressed: () {
|
||||
final newVal = !enableShowDanmaku;
|
||||
plPlayerController.enableShowDanmaku.value = newVal;
|
||||
if (!Pref.tempPlayerConf) {
|
||||
if (!plPlayerController.tempPlayerConf) {
|
||||
GStorage.setting.put(
|
||||
SettingBoxKey.enableShowDanmaku,
|
||||
newVal,
|
||||
|
||||
@@ -26,6 +26,7 @@ class PgcIntroPage extends StatefulWidget {
|
||||
final String heroTag;
|
||||
final Function showEpisodes;
|
||||
final Function showIntroDetail;
|
||||
final double maxWidth;
|
||||
|
||||
const PgcIntroPage({
|
||||
super.key,
|
||||
@@ -33,14 +34,16 @@ class PgcIntroPage extends StatefulWidget {
|
||||
required this.heroTag,
|
||||
required this.showEpisodes,
|
||||
required this.showIntroDetail,
|
||||
required this.maxWidth,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PgcIntroPage> createState() => _PgcIntroPageState();
|
||||
}
|
||||
|
||||
class _PgcIntroPageState extends State<PgcIntroPage>
|
||||
class _PgcIntroPageState extends TripleState<PgcIntroPage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
late PgcIntroController introController;
|
||||
late VideoDetailController videoDetailCtr;
|
||||
|
||||
@@ -89,12 +92,15 @@ class _PgcIntroPageState extends State<PgcIntroPage>
|
||||
),
|
||||
);
|
||||
if (!introController.isPgc) {
|
||||
sliver = SliverMainAxisGroup(
|
||||
slivers: [
|
||||
sliver,
|
||||
?_buildBreif(item),
|
||||
],
|
||||
);
|
||||
final breif = _buildBreif(item);
|
||||
if (breif != null) {
|
||||
sliver = SliverMainAxisGroup(
|
||||
slivers: [
|
||||
sliver,
|
||||
breif,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
return SliverPadding(
|
||||
padding:
|
||||
@@ -107,32 +113,28 @@ class _PgcIntroPageState extends State<PgcIntroPage>
|
||||
Widget? _buildBreif(PgcInfoModel item) {
|
||||
final img = item.brief?.img;
|
||||
if (img != null && img.isNotEmpty) {
|
||||
return SliverLayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final maxWidth = constraints.crossAxisExtent;
|
||||
double padding = max(0, maxWidth - 400);
|
||||
final imgWidth = maxWidth - padding;
|
||||
padding = padding / 2;
|
||||
return SliverPadding(
|
||||
padding: EdgeInsetsGeometry.only(
|
||||
top: 10,
|
||||
left: padding,
|
||||
right: padding,
|
||||
),
|
||||
sliver: SliverMainAxisGroup(
|
||||
slivers: img.map((e) {
|
||||
return SliverToBoxAdapter(
|
||||
child: NetworkImgLayer(
|
||||
radius: 0,
|
||||
src: e.url,
|
||||
width: imgWidth,
|
||||
height: imgWidth * e.aspectRatio,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
final maxWidth = widget.maxWidth - 2 * StyleString.safeSpace;
|
||||
double padding = max(0, maxWidth - 400);
|
||||
final imgWidth = maxWidth - padding;
|
||||
padding = padding / 2;
|
||||
return SliverPadding(
|
||||
padding: EdgeInsetsGeometry.only(
|
||||
top: 10,
|
||||
left: padding,
|
||||
right: padding,
|
||||
),
|
||||
sliver: SliverMainAxisGroup(
|
||||
slivers: img.map((e) {
|
||||
return SliverToBoxAdapter(
|
||||
child: NetworkImgLayer(
|
||||
radius: 0,
|
||||
src: e.url,
|
||||
width: imgWidth,
|
||||
height: imgWidth * e.aspectRatio,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -404,75 +406,65 @@ class _PgcIntroPageState extends State<PgcIntroPage>
|
||||
) {
|
||||
return SizedBox(
|
||||
height: 48,
|
||||
child: TripleBuilder(
|
||||
introController: introController,
|
||||
builder: (context, tripleAnimation, onStartTriple, onCancelTriple) {
|
||||
return Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
||||
onTap: () => introController.handleAction(
|
||||
introController.actionLikeVideo,
|
||||
),
|
||||
selectStatus: introController.hasLike.value,
|
||||
semanticsLabel: '点赞',
|
||||
text: NumUtil.numFormat(item.stat!.like),
|
||||
onStartTriple: onStartTriple,
|
||||
onCancelTriple: onCancelTriple,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
||||
selectStatus: introController.hasLike.value,
|
||||
semanticsLabel: '点赞',
|
||||
text: NumUtil.numFormat(item.stat!.like),
|
||||
onStartTriple: onStartTriple,
|
||||
onCancelTriple: onCancelTriple,
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.b),
|
||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
||||
onTap: introController.actionCoinVideo,
|
||||
selectStatus: introController.hasCoin,
|
||||
semanticsLabel: '投币',
|
||||
text: NumUtil.numFormat(item.stat!.coin),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||
onTap: () => introController.showFavBottomSheet(context),
|
||||
onLongPress: () => introController.showFavBottomSheet(
|
||||
context,
|
||||
isLongPress: true,
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.b),
|
||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
||||
onTap: () => introController.handleAction(
|
||||
introController.actionCoinVideo,
|
||||
),
|
||||
selectStatus: introController.hasCoin,
|
||||
semanticsLabel: '投币',
|
||||
text: NumUtil.numFormat(item.stat!.coin),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||
onTap: () => introController.showFavBottomSheet(context),
|
||||
onLongPress: () => introController.showFavBottomSheet(
|
||||
context,
|
||||
isLongPress: true,
|
||||
),
|
||||
selectStatus: introController.hasFav.value,
|
||||
semanticsLabel: '收藏',
|
||||
text: NumUtil.numFormat(item.stat!.favorite),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.clock),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidClock),
|
||||
onTap: () =>
|
||||
introController.handleAction(introController.viewLater),
|
||||
selectStatus: introController.hasLater.value,
|
||||
semanticsLabel: '再看',
|
||||
text: '再看',
|
||||
),
|
||||
),
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
||||
onTap: () => introController.actionShareVideo(context),
|
||||
selectStatus: false,
|
||||
semanticsLabel: '转发',
|
||||
text: NumUtil.numFormat(item.stat!.share),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
selectStatus: introController.hasFav.value,
|
||||
semanticsLabel: '收藏',
|
||||
text: NumUtil.numFormat(item.stat!.favorite),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.clock),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidClock),
|
||||
onTap: () =>
|
||||
introController.handleAction(introController.viewLater),
|
||||
selectStatus: introController.hasLater.value,
|
||||
semanticsLabel: '再看',
|
||||
text: '再看',
|
||||
),
|
||||
),
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
||||
onTap: () => introController.actionShareVideo(context),
|
||||
selectStatus: false,
|
||||
semanticsLabel: '转发',
|
||||
text: NumUtil.numFormat(item.stat!.share),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,18 +41,21 @@ class UgcIntroPanel extends StatefulWidget {
|
||||
required this.showAiBottomSheet,
|
||||
required this.showEpisodes,
|
||||
required this.onShowMemberPage,
|
||||
required this.isHorizontal,
|
||||
});
|
||||
final String heroTag;
|
||||
final Function showAiBottomSheet;
|
||||
final Function showEpisodes;
|
||||
final ValueChanged<int?> onShowMemberPage;
|
||||
final bool isHorizontal;
|
||||
|
||||
@override
|
||||
State<UgcIntroPanel> createState() => _UgcIntroPanelState();
|
||||
}
|
||||
|
||||
class _UgcIntroPanelState extends State<UgcIntroPanel>
|
||||
class _UgcIntroPanelState extends TripleState<UgcIntroPanel>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
late UgcIntroController introController;
|
||||
late final VideoDetailController videoDetailCtr =
|
||||
Get.find<VideoDetailController>(tag: widget.heroTag);
|
||||
@@ -75,267 +78,258 @@ class _UgcIntroPanelState extends State<UgcIntroPanel>
|
||||
sizeCurve: Curves.linear,
|
||||
);
|
||||
final isPortrait = context.isPortrait;
|
||||
return SliverLayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
bool isHorizontal =
|
||||
!isPortrait &&
|
||||
constraints.crossAxisExtent >
|
||||
constraints.viewportMainAxisExtent * 1.25;
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
top: 10,
|
||||
),
|
||||
sliver: Obx(
|
||||
() {
|
||||
VideoDetailData videoDetail = introController.videoDetail.value;
|
||||
bool isLoading = videoDetail.bvid == null;
|
||||
int? mid = videoDetail.owner?.mid;
|
||||
return SliverToBoxAdapter(
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
feedBack();
|
||||
introController.expandableCtr.toggle();
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {},
|
||||
child: Row(
|
||||
children: [
|
||||
if (videoDetail.staff.isNullOrEmpty) ...[
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _buildAvatar(
|
||||
theme,
|
||||
() {
|
||||
if (mid != null) {
|
||||
feedBack();
|
||||
if (!isPortrait &&
|
||||
introController
|
||||
.horizontalMemberPage) {
|
||||
widget.onShowMemberPage(mid);
|
||||
} else {
|
||||
Get.toNamed(
|
||||
'/member?mid=$mid&from_view_aid=${videoDetailCtr.aid}',
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
followButton(context, theme),
|
||||
] else
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: ReloadScrollPhysics(
|
||||
controller: introController,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 25,
|
||||
children: videoDetail.staff!
|
||||
.map(
|
||||
(e) => _buildStaff(
|
||||
theme,
|
||||
isPortrait,
|
||||
mid,
|
||||
e,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isHorizontal) ...[
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: actionGrid(
|
||||
context,
|
||||
isLoading,
|
||||
videoDetail,
|
||||
introController,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (isLoading)
|
||||
_buildVideoTitle(theme, videoDetail)
|
||||
else
|
||||
ExpandablePanel(
|
||||
controller: introController.expandableCtr,
|
||||
collapsed: GestureDetector(
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
Utils.copyText(videoDetail.title ?? '');
|
||||
},
|
||||
child: _buildVideoTitle(theme, videoDetail),
|
||||
),
|
||||
expanded: GestureDetector(
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
Utils.copyText(videoDetail.title ?? '');
|
||||
},
|
||||
child: _buildVideoTitle(
|
||||
theme,
|
||||
videoDetail,
|
||||
isExpand: true,
|
||||
),
|
||||
),
|
||||
theme: expandTheme,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
_buildInfo(theme, videoDetail),
|
||||
if (introController.enableAi) _aiBtn,
|
||||
],
|
||||
),
|
||||
if (videoDetail.argueInfo?.argueMsg?.isNotEmpty == true &&
|
||||
introController.showArgueMsg) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Icon(
|
||||
size: 13,
|
||||
Icons.error_outline,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
const WidgetSpan(child: SizedBox(width: 2)),
|
||||
TextSpan(
|
||||
text: '${videoDetail.argueInfo!.argueMsg}',
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
ExpandablePanel(
|
||||
controller: introController.expandableCtr,
|
||||
collapsed: const SizedBox.shrink(),
|
||||
expanded: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () =>
|
||||
Utils.copyText('${videoDetail.bvid}'),
|
||||
child: Text(
|
||||
videoDetail.bvid ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (videoDetail.descV2?.isNotEmpty == true) ...[
|
||||
const SizedBox(height: 8),
|
||||
SelectableText.rich(
|
||||
style: const TextStyle(
|
||||
height: 1.4,
|
||||
),
|
||||
TextSpan(
|
||||
children: [
|
||||
buildContent(theme, videoDetail),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
Obx(() {
|
||||
final videoTags = introController.videoTags.value;
|
||||
if (videoTags.isNullOrEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return _buildTags(videoTags!);
|
||||
}),
|
||||
],
|
||||
),
|
||||
theme: expandTheme,
|
||||
),
|
||||
Obx(
|
||||
() => introController.status.value
|
||||
? const SizedBox.shrink()
|
||||
: Center(
|
||||
child: TextButton.icon(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
introController
|
||||
..status.value = true
|
||||
..queryVideoIntro();
|
||||
if (videoDetailCtr.videoUrl.isNullOrEmpty &&
|
||||
!videoDetailCtr.isQuerying) {
|
||||
videoDetailCtr.queryVideoUrl();
|
||||
final isHorizontal = !isPortrait && widget.isHorizontal;
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
top: 10,
|
||||
),
|
||||
sliver: Obx(
|
||||
() {
|
||||
VideoDetailData videoDetail = introController.videoDetail.value;
|
||||
bool isLoading = videoDetail.bvid == null;
|
||||
int? mid = videoDetail.owner?.mid;
|
||||
return SliverToBoxAdapter(
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
feedBack();
|
||||
introController.expandableCtr.toggle();
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {},
|
||||
child: Row(
|
||||
children: [
|
||||
if (videoDetail.staff.isNullOrEmpty) ...[
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _buildAvatar(
|
||||
theme,
|
||||
() {
|
||||
if (mid != null) {
|
||||
feedBack();
|
||||
if (!isPortrait &&
|
||||
introController.horizontalMemberPage) {
|
||||
widget.onShowMemberPage(mid);
|
||||
} else {
|
||||
Get.toNamed(
|
||||
'/member?mid=$mid&from_view_aid=${videoDetailCtr.aid}',
|
||||
);
|
||||
}
|
||||
},
|
||||
label: const Text("点此重新加载"),
|
||||
),
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
followButton(context, theme),
|
||||
] else
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: ReloadScrollPhysics(
|
||||
controller: introController,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 25,
|
||||
children: videoDetail.staff!
|
||||
.map(
|
||||
(e) => _buildStaff(
|
||||
theme,
|
||||
isPortrait,
|
||||
mid,
|
||||
e,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isHorizontal) ...[
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: actionGrid(
|
||||
context,
|
||||
isLoading,
|
||||
videoDetail,
|
||||
introController,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (isLoading)
|
||||
_buildVideoTitle(theme, videoDetail)
|
||||
else
|
||||
ExpandablePanel(
|
||||
controller: introController.expandableCtr,
|
||||
collapsed: GestureDetector(
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
Utils.copyText(videoDetail.title ?? '');
|
||||
},
|
||||
child: _buildVideoTitle(theme, videoDetail),
|
||||
),
|
||||
// 点赞收藏转发 布局样式2
|
||||
if (!isHorizontal) ...[
|
||||
const SizedBox(height: 8),
|
||||
actionGrid(
|
||||
context,
|
||||
isLoading,
|
||||
expanded: GestureDetector(
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
Utils.copyText(videoDetail.title ?? '');
|
||||
},
|
||||
child: _buildVideoTitle(
|
||||
theme,
|
||||
videoDetail,
|
||||
introController,
|
||||
isExpand: true,
|
||||
),
|
||||
],
|
||||
// 合集
|
||||
if (!isLoading &&
|
||||
videoDetail.ugcSeason != null &&
|
||||
(isPortrait ||
|
||||
!videoDetailCtr
|
||||
.plPlayerController
|
||||
.horizontalSeasonPanel))
|
||||
SeasonPanel(
|
||||
heroTag: widget.heroTag,
|
||||
showEpisodes: widget.showEpisodes,
|
||||
ugcIntroController: introController,
|
||||
),
|
||||
if (!isLoading &&
|
||||
videoDetail.pages != null &&
|
||||
videoDetail.pages!.length > 1 &&
|
||||
(isPortrait ||
|
||||
!videoDetailCtr
|
||||
.plPlayerController
|
||||
.horizontalSeasonPanel)) ...[
|
||||
PagesPanel(
|
||||
heroTag: widget.heroTag,
|
||||
ugcIntroController: introController,
|
||||
bvid: introController.bvid,
|
||||
showEpisodes: widget.showEpisodes,
|
||||
),
|
||||
],
|
||||
),
|
||||
theme: expandTheme,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
_buildInfo(theme, videoDetail),
|
||||
if (introController.enableAi) _aiBtn,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
if (videoDetail.argueInfo?.argueMsg?.isNotEmpty == true &&
|
||||
introController.showArgueMsg) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Icon(
|
||||
size: 13,
|
||||
Icons.error_outline,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
const WidgetSpan(child: SizedBox(width: 2)),
|
||||
TextSpan(
|
||||
text: '${videoDetail.argueInfo!.argueMsg}',
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
ExpandablePanel(
|
||||
controller: introController.expandableCtr,
|
||||
collapsed: const SizedBox.shrink(),
|
||||
expanded: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () => Utils.copyText('${videoDetail.bvid}'),
|
||||
child: Text(
|
||||
videoDetail.bvid ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (videoDetail.descV2?.isNotEmpty == true) ...[
|
||||
const SizedBox(height: 8),
|
||||
SelectableText.rich(
|
||||
style: const TextStyle(
|
||||
height: 1.4,
|
||||
),
|
||||
TextSpan(
|
||||
children: [
|
||||
buildContent(theme, videoDetail),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
Obx(() {
|
||||
final videoTags = introController.videoTags.value;
|
||||
if (videoTags.isNullOrEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return _buildTags(videoTags!);
|
||||
}),
|
||||
],
|
||||
),
|
||||
theme: expandTheme,
|
||||
),
|
||||
Obx(
|
||||
() => introController.status.value
|
||||
? const SizedBox.shrink()
|
||||
: Center(
|
||||
child: TextButton.icon(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
introController
|
||||
..status.value = true
|
||||
..queryVideoIntro();
|
||||
if (videoDetailCtr.videoUrl.isNullOrEmpty &&
|
||||
!videoDetailCtr.isQuerying) {
|
||||
videoDetailCtr.queryVideoUrl();
|
||||
}
|
||||
},
|
||||
label: const Text("点此重新加载"),
|
||||
),
|
||||
),
|
||||
),
|
||||
// 点赞收藏转发 布局样式2
|
||||
if (!isHorizontal) ...[
|
||||
const SizedBox(height: 8),
|
||||
actionGrid(
|
||||
context,
|
||||
isLoading,
|
||||
videoDetail,
|
||||
introController,
|
||||
),
|
||||
],
|
||||
// 合集
|
||||
if (!isLoading &&
|
||||
videoDetail.ugcSeason != null &&
|
||||
(isPortrait ||
|
||||
!videoDetailCtr
|
||||
.plPlayerController
|
||||
.horizontalSeasonPanel))
|
||||
SeasonPanel(
|
||||
heroTag: widget.heroTag,
|
||||
showEpisodes: widget.showEpisodes,
|
||||
ugcIntroController: introController,
|
||||
),
|
||||
if (!isLoading &&
|
||||
videoDetail.pages != null &&
|
||||
videoDetail.pages!.length > 1 &&
|
||||
(isPortrait ||
|
||||
!videoDetailCtr
|
||||
.plPlayerController
|
||||
.horizontalSeasonPanel)) ...[
|
||||
PagesPanel(
|
||||
heroTag: widget.heroTag,
|
||||
ugcIntroController: introController,
|
||||
bvid: introController.bvid,
|
||||
showEpisodes: widget.showEpisodes,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -506,95 +500,85 @@ class _UgcIntroPanelState extends State<UgcIntroPanel>
|
||||
) {
|
||||
return SizedBox(
|
||||
height: 48,
|
||||
child: TripleBuilder(
|
||||
introController: introController,
|
||||
builder: (context, tripleAnimation, onStartTriple, onCancelTriple) {
|
||||
return Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
||||
onTap: () => introController.handleAction(
|
||||
introController.actionLikeVideo,
|
||||
),
|
||||
selectStatus: introController.hasLike.value,
|
||||
semanticsLabel: '点赞',
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.like)
|
||||
: null,
|
||||
onStartTriple: onStartTriple,
|
||||
onCancelTriple: onCancelTriple,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
||||
selectStatus: introController.hasLike.value,
|
||||
semanticsLabel: '点赞',
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.like)
|
||||
: null,
|
||||
onStartTriple: onStartTriple,
|
||||
onCancelTriple: onCancelTriple,
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.thumbsDown),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown),
|
||||
onTap: () => introController.handleAction(
|
||||
introController.actionDislikeVideo,
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.thumbsDown),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown),
|
||||
onTap: () => introController.handleAction(
|
||||
introController.actionDislikeVideo,
|
||||
),
|
||||
selectStatus: introController.hasDislike.value,
|
||||
semanticsLabel: '点踩',
|
||||
text: "点踩",
|
||||
),
|
||||
selectStatus: introController.hasDislike.value,
|
||||
semanticsLabel: '点踩',
|
||||
text: "点踩",
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.b),
|
||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
||||
onTap: introController.actionCoinVideo,
|
||||
selectStatus: introController.hasCoin,
|
||||
semanticsLabel: '投币',
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.coin)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||
onTap: () => introController.showFavBottomSheet(context),
|
||||
onLongPress: () => introController.showFavBottomSheet(
|
||||
context,
|
||||
isLongPress: true,
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.b),
|
||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
||||
onTap: () => introController.handleAction(
|
||||
introController.actionCoinVideo,
|
||||
),
|
||||
selectStatus: introController.hasCoin,
|
||||
semanticsLabel: '投币',
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.coin)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||
onTap: () => introController.showFavBottomSheet(context),
|
||||
onLongPress: () => introController.showFavBottomSheet(
|
||||
context,
|
||||
isLongPress: true,
|
||||
),
|
||||
selectStatus: introController.hasFav.value,
|
||||
semanticsLabel: '收藏',
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.favorite)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.clock),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidClock),
|
||||
onTap: () =>
|
||||
introController.handleAction(introController.viewLater),
|
||||
selectStatus: introController.hasLater.value,
|
||||
semanticsLabel: '再看',
|
||||
text: '再看',
|
||||
),
|
||||
),
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
||||
onTap: () => introController.actionShareVideo(context),
|
||||
selectStatus: false,
|
||||
semanticsLabel: '分享',
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.share!)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
selectStatus: introController.hasFav.value,
|
||||
semanticsLabel: '收藏',
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.favorite)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.clock),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidClock),
|
||||
onTap: () =>
|
||||
introController.handleAction(introController.viewLater),
|
||||
selectStatus: introController.hasLater.value,
|
||||
semanticsLabel: '再看',
|
||||
text: '再看',
|
||||
),
|
||||
),
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
||||
onTap: () => introController.actionShareVideo(context),
|
||||
selectStatus: false,
|
||||
semanticsLabel: '分享',
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.share!)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,9 +79,7 @@ class ActionItem extends StatelessWidget {
|
||||
)
|
||||
: child,
|
||||
);
|
||||
return expand
|
||||
? Expanded(child: child)
|
||||
: Material(type: MaterialType.transparency, child: child);
|
||||
return expand ? Expanded(child: child) : child;
|
||||
}
|
||||
|
||||
Widget _buildText(ThemeData theme) {
|
||||
@@ -135,7 +133,7 @@ class _ArcPainter extends CustomPainter {
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
||||
return false;
|
||||
bool shouldRepaint(covariant _ArcPainter oldDelegate) {
|
||||
return sweepAngle != oldDelegate.sweepAngle || color != oldDelegate.color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,29 +9,26 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
abstract class TripleState<T extends StatefulWidget> extends State<T>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final tripleAnimCtr = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 1000),
|
||||
reverseDuration: const Duration(milliseconds: 400),
|
||||
);
|
||||
CommonIntroController get introController;
|
||||
|
||||
late final tripleAnimation = Tween<double>(
|
||||
// no need for pugv
|
||||
AnimationController? _tripleAnimCtr;
|
||||
Animation<double>? _tripleAnimation;
|
||||
|
||||
AnimationController get tripleAnimCtr =>
|
||||
_tripleAnimCtr ??= AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 1200),
|
||||
reverseDuration: const Duration(milliseconds: 400),
|
||||
);
|
||||
|
||||
Animation<double> get tripleAnimation => _tripleAnimation ??= Tween<double>(
|
||||
begin: 0,
|
||||
end: -2 * pi,
|
||||
).animate(CurvedAnimation(parent: tripleAnimCtr, curve: Curves.easeInOut));
|
||||
|
||||
CommonIntroController get introController;
|
||||
|
||||
Timer? _timer;
|
||||
|
||||
// @mustCallSuper
|
||||
// void tripleListener(AnimationStatus status) {
|
||||
// if (status == AnimationStatus.completed) {
|
||||
// tripleAnimCtr.reset();
|
||||
// onTriple();
|
||||
// }
|
||||
// }
|
||||
|
||||
void _cancelTimer() {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
@@ -40,14 +37,14 @@ abstract class TripleState<T extends StatefulWidget> extends State<T>
|
||||
@override
|
||||
void dispose() {
|
||||
_cancelTimer();
|
||||
tripleAnimCtr.dispose();
|
||||
_tripleAnimCtr?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void onStartTriple() {
|
||||
_timer ??= Timer(const Duration(milliseconds: 200), () {
|
||||
HapticFeedback.lightImpact();
|
||||
if (introController.hasTriple) {
|
||||
HapticFeedback.lightImpact();
|
||||
SmartDialog.showToast('已完成三连');
|
||||
} else {
|
||||
tripleAnimCtr.forward().whenComplete(() {
|
||||
@@ -71,39 +68,3 @@ abstract class TripleState<T extends StatefulWidget> extends State<T>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TripleBuilder extends StatefulWidget {
|
||||
const TripleBuilder({
|
||||
super.key,
|
||||
required this.builder,
|
||||
required this.introController,
|
||||
// this.tripleListener,
|
||||
});
|
||||
final CommonIntroController introController;
|
||||
final Widget Function(
|
||||
BuildContext context,
|
||||
Animation<double> tripleAnimation,
|
||||
void Function() onStartTriple,
|
||||
void Function([bool]) onCancelTriple,
|
||||
)
|
||||
builder;
|
||||
// final AnimationStatusListener? tripleListener;
|
||||
|
||||
@override
|
||||
State<TripleBuilder> createState() => _TripleBuilderState();
|
||||
}
|
||||
|
||||
class _TripleBuilderState extends TripleState<TripleBuilder> {
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
widget.builder(context, tripleAnimation, onStartTriple, onCancelTriple);
|
||||
|
||||
@override
|
||||
late final CommonIntroController introController = widget.introController;
|
||||
|
||||
// @override
|
||||
// void tripleListener(AnimationStatus status) {
|
||||
// super.tripleListener(status);
|
||||
// widget.tripleListener?.call(status);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -43,14 +43,12 @@ import 'package:PiliPlus/plugin/pl_player/view.dart';
|
||||
import 'package:PiliPlus/services/service_locator.dart';
|
||||
import 'package:PiliPlus/services/shutdown_timer_service.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/image_util.dart';
|
||||
import 'package:PiliPlus/utils/num_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:auto_orientation/auto_orientation.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
@@ -105,19 +103,17 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
bool get isFullScreen => plPlayerController?.isFullScreen.value ?? false;
|
||||
|
||||
bool get _shouldShowSeasonPanel {
|
||||
if (!videoDetailController.isUgc) {
|
||||
if (isPortrait || !videoDetailController.isUgc) {
|
||||
return false;
|
||||
}
|
||||
late final videoDetail = ugcIntroController.videoDetail.value;
|
||||
return videoDetailController.plPlayerController.horizontalSeasonPanel &&
|
||||
(videoDetail.ugcSeason != null ||
|
||||
((videoDetail.pages?.length ?? 0) > 1)) &&
|
||||
context.isLandscape;
|
||||
((videoDetail.pages?.length ?? 0) > 1));
|
||||
}
|
||||
|
||||
bool get _horizontalPreview =>
|
||||
videoDetailController.plPlayerController.horizontalPreview &&
|
||||
context.isLandscape;
|
||||
!isPortrait && videoDetailController.plPlayerController.horizontalPreview;
|
||||
|
||||
StreamSubscription? _listenerFS;
|
||||
|
||||
@@ -203,11 +199,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
final isVertical = videoDetailController.isVertical.value;
|
||||
final mode = plPlayerController?.mode;
|
||||
|
||||
late final size = Get.size;
|
||||
if (!(mode == FullScreenMode.vertical ||
|
||||
(mode == FullScreenMode.auto && isVertical) ||
|
||||
(mode == FullScreenMode.ratio &&
|
||||
(isVertical || size.height / size.width < 1.25)))) {
|
||||
(isVertical || maxHeight / maxWidth < 1.25)))) {
|
||||
landScape();
|
||||
}
|
||||
});
|
||||
@@ -543,11 +538,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
}
|
||||
|
||||
Widget get childWhenDisabled {
|
||||
final isPortrait = context.isPortrait;
|
||||
final isFullScreen = this.isFullScreen;
|
||||
final useSafeArea = !removeSafeArea && isPortrait && isFullScreen;
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
final double width = size.width;
|
||||
final double height = size.height;
|
||||
return SafeArea(
|
||||
top: useSafeArea,
|
||||
bottom: useSafeArea,
|
||||
@@ -611,8 +603,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
controller: videoDetailController.scrollCtr,
|
||||
onlyOneScrollInBody: true,
|
||||
pinnedHeaderSliverHeightBuilder: () {
|
||||
double pinnedHeight = isFullScreen || context.isLandscape
|
||||
? MediaQuery.sizeOf(context).height
|
||||
double pinnedHeight = this.isFullScreen || !isPortrait
|
||||
? maxHeight
|
||||
: videoDetailController.isExpanding ||
|
||||
videoDetailController.isCollapsing
|
||||
? animHeight
|
||||
@@ -638,6 +630,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
return pinnedHeight;
|
||||
},
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
return [
|
||||
SliverAppBar(
|
||||
elevation: 0,
|
||||
@@ -646,7 +639,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
automaticallyImplyLeading: false,
|
||||
pinned: true,
|
||||
expandedHeight: isFullScreen || !isPortrait
|
||||
? height
|
||||
? maxHeight
|
||||
: videoDetailController.isExpanding ||
|
||||
videoDetailController.isCollapsing
|
||||
? animHeight
|
||||
@@ -674,21 +667,19 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
}
|
||||
return SizedBox(
|
||||
height: !isPortrait || isFullScreen
|
||||
? height -
|
||||
? maxHeight -
|
||||
(!isPortrait || removeSafeArea
|
||||
? 0
|
||||
: MediaQuery.paddingOf(
|
||||
this.context,
|
||||
).top)
|
||||
: padding.top)
|
||||
: videoDetailController.isExpanding ||
|
||||
videoDetailController.isCollapsing
|
||||
? animHeight
|
||||
: videoDetailController.videoHeight,
|
||||
width: width,
|
||||
width: maxWidth,
|
||||
child: videoPlayer(
|
||||
width,
|
||||
maxWidth,
|
||||
!isPortrait || isFullScreen
|
||||
? height
|
||||
? maxHeight
|
||||
: videoDetailController.isExpanding ||
|
||||
videoDetailController.isCollapsing
|
||||
? animHeight
|
||||
@@ -952,7 +943,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
child: videoTabBarView(
|
||||
controller: videoDetailController.tabCtr,
|
||||
children: [
|
||||
videoIntro(true, false),
|
||||
videoIntro(isHorizontal: false, needCtr: false),
|
||||
if (videoDetailController.showReply)
|
||||
videoReplyPanel(false),
|
||||
if (_shouldShowSeasonPanel) seasonPanel,
|
||||
@@ -969,20 +960,17 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
|
||||
Widget get childWhenDisabledAlmostSquareInner => Obx(
|
||||
() {
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
final double width = size.width;
|
||||
final double height = size.height;
|
||||
final padding = MediaQuery.paddingOf(context);
|
||||
final isFullScreen = this.isFullScreen;
|
||||
if (videoDetailController.isVertical.value && enableVerticalExpand) {
|
||||
final double videoHeight =
|
||||
height - (removeSafeArea ? 0 : padding.vertical);
|
||||
maxHeight - (removeSafeArea ? 0 : padding.vertical);
|
||||
final double videoWidth = videoHeight * 9 / 16;
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: videoHeight,
|
||||
width: isFullScreen ? width : videoWidth,
|
||||
width: isFullScreen ? maxWidth : videoWidth,
|
||||
child: videoPlayer(videoWidth, videoHeight),
|
||||
),
|
||||
Expanded(
|
||||
@@ -997,7 +985,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
child: videoTabBarView(
|
||||
controller: videoDetailController.tabCtr,
|
||||
children: [
|
||||
videoIntro(),
|
||||
videoIntro(
|
||||
width: maxWidth - videoWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
if (videoDetailController.showReply)
|
||||
videoReplyPanel(),
|
||||
if (_shouldShowSeasonPanel) seasonPanel,
|
||||
@@ -1011,15 +1002,16 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
],
|
||||
);
|
||||
}
|
||||
final double videoHeight = height / 2.5;
|
||||
final double videoHeight = maxHeight / 2.5;
|
||||
final shouldShowSeasonPanel = _shouldShowSeasonPanel;
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: width,
|
||||
width: maxWidth,
|
||||
height: isFullScreen
|
||||
? height - (removeSafeArea ? 0 : padding.vertical)
|
||||
? maxHeight - (removeSafeArea ? 0 : padding.vertical)
|
||||
: videoHeight,
|
||||
child: videoPlayer(width, videoHeight),
|
||||
child: videoPlayer(maxWidth, videoHeight),
|
||||
),
|
||||
Expanded(
|
||||
child: Scaffold(
|
||||
@@ -1032,11 +1024,20 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: videoIntro()),
|
||||
Expanded(
|
||||
child: videoIntro(
|
||||
width: () {
|
||||
double flex = 1;
|
||||
if (videoDetailController.showReply) flex++;
|
||||
if (shouldShowSeasonPanel) flex++;
|
||||
return maxWidth / flex;
|
||||
}(),
|
||||
height: maxHeight - videoHeight,
|
||||
),
|
||||
),
|
||||
if (videoDetailController.showReply)
|
||||
Expanded(child: videoReplyPanel()),
|
||||
if (_shouldShowSeasonPanel)
|
||||
Expanded(child: seasonPanel),
|
||||
if (shouldShowSeasonPanel) Expanded(child: seasonPanel),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -1051,21 +1052,23 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
|
||||
Widget get childWhenDisabledLandscapeInner => Obx(
|
||||
() {
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
final double width = size.width;
|
||||
final double height = size.height;
|
||||
final padding = MediaQuery.paddingOf(context);
|
||||
final isFullScreen = this.isFullScreen;
|
||||
if (videoDetailController.isVertical.value && enableVerticalExpand) {
|
||||
final double videoHeight = height - (removeSafeArea ? 0 : padding.top);
|
||||
final double videoHeight =
|
||||
maxHeight - (removeSafeArea ? 0 : padding.top);
|
||||
final double videoWidth = videoHeight * 9 / 16;
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: isFullScreen ? const SizedBox.shrink() : videoIntro(),
|
||||
),
|
||||
if (!isFullScreen)
|
||||
Expanded(
|
||||
child: videoIntro(
|
||||
width: (maxWidth - videoWidth) / 2,
|
||||
height: maxHeight,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: videoHeight,
|
||||
width: isFullScreen ? width : videoWidth,
|
||||
width: isFullScreen ? maxWidth : videoWidth,
|
||||
child: videoPlayer(videoWidth, videoHeight),
|
||||
),
|
||||
Expanded(
|
||||
@@ -1093,27 +1096,34 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
],
|
||||
);
|
||||
}
|
||||
double videoWidth = clampDouble(height / width * 1.08, 0.5, 0.7) * width;
|
||||
if (width >= 560) {
|
||||
videoWidth = min(videoWidth, width - 280);
|
||||
double videoWidth =
|
||||
clampDouble(maxHeight / maxWidth * 1.08, 0.5, 0.7) * maxWidth;
|
||||
if (maxWidth >= 560) {
|
||||
videoWidth = min(videoWidth, maxWidth - 280);
|
||||
}
|
||||
final double videoHeight = videoWidth * 9 / 16;
|
||||
final introHeight =
|
||||
maxHeight - videoHeight - (removeSafeArea ? 0 : padding.top);
|
||||
return Row(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: isFullScreen ? width : videoWidth,
|
||||
height: isFullScreen ? height : videoHeight,
|
||||
width: isFullScreen ? maxWidth : videoWidth,
|
||||
height: isFullScreen ? maxHeight : videoHeight,
|
||||
child: videoPlayer(videoWidth, videoHeight),
|
||||
),
|
||||
Offstage(
|
||||
offstage: isFullScreen,
|
||||
child: SizedBox(
|
||||
width: videoWidth,
|
||||
height:
|
||||
height - videoHeight - (removeSafeArea ? 0 : padding.top),
|
||||
child: videoIntro(false, false),
|
||||
height: introHeight,
|
||||
child: videoIntro(
|
||||
width: videoWidth,
|
||||
height: introHeight,
|
||||
needRelated: false,
|
||||
needCtr: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1122,10 +1132,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
offstage: isFullScreen,
|
||||
child: SizedBox(
|
||||
width:
|
||||
width -
|
||||
maxWidth -
|
||||
videoWidth -
|
||||
(removeSafeArea ? 0 : padding.horizontal),
|
||||
height: height - (removeSafeArea ? 0 : padding.top),
|
||||
height: maxHeight - (removeSafeArea ? 0 : padding.top),
|
||||
child: Scaffold(
|
||||
key: videoDetailController.childKey,
|
||||
resizeToAvoidBottomInset: false,
|
||||
@@ -1173,46 +1183,52 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
},
|
||||
);
|
||||
|
||||
Widget get childWhenDisabledLandscape => Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
key: videoDetailController.scaffoldKey,
|
||||
appBar: (removeSafeArea || isFullScreen)
|
||||
? null
|
||||
: AppBar(
|
||||
backgroundColor: Colors.black,
|
||||
toolbarHeight: 0,
|
||||
),
|
||||
body: SafeArea(
|
||||
left: !removeSafeArea && !isFullScreen,
|
||||
right: !removeSafeArea && !isFullScreen,
|
||||
top: !removeSafeArea && !isFullScreen,
|
||||
bottom: false,
|
||||
child: childWhenDisabledLandscapeInner,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
Widget get childWhenDisabledAlmostSquare => Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
key: videoDetailController.scaffoldKey,
|
||||
appBar: (removeSafeArea || isFullScreen)
|
||||
? null
|
||||
: AppBar(
|
||||
backgroundColor: Colors.black,
|
||||
toolbarHeight: 0,
|
||||
Widget get childWhenDisabledLandscape {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
key: videoDetailController.scaffoldKey,
|
||||
appBar: (removeSafeArea || isFullScreen)
|
||||
? null
|
||||
: AppBar(
|
||||
backgroundColor: Colors.black,
|
||||
toolbarHeight: 0,
|
||||
),
|
||||
body: SafeArea(
|
||||
left: !removeSafeArea && !isFullScreen,
|
||||
right: !removeSafeArea && !isFullScreen,
|
||||
top: !removeSafeArea && !isFullScreen,
|
||||
bottom: false,
|
||||
child: childWhenDisabledLandscapeInner,
|
||||
),
|
||||
body: SafeArea(
|
||||
left: !removeSafeArea && !isFullScreen,
|
||||
right: !removeSafeArea && !isFullScreen,
|
||||
top: !removeSafeArea && !isFullScreen,
|
||||
bottom: false,
|
||||
child: childWhenDisabledAlmostSquareInner,
|
||||
),
|
||||
);
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget get childWhenDisabledAlmostSquare {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
key: videoDetailController.scaffoldKey,
|
||||
appBar: (removeSafeArea || isFullScreen)
|
||||
? null
|
||||
: AppBar(
|
||||
backgroundColor: Colors.black,
|
||||
toolbarHeight: 0,
|
||||
),
|
||||
body: SafeArea(
|
||||
left: !removeSafeArea && !isFullScreen,
|
||||
right: !removeSafeArea && !isFullScreen,
|
||||
top: !removeSafeArea && !isFullScreen,
|
||||
bottom: false,
|
||||
child: childWhenDisabledAlmostSquareInner,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget get manualPlayerWidget => Obx(() {
|
||||
if (!videoDetailController.autoPlay.value) {
|
||||
@@ -1400,28 +1416,33 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
}
|
||||
|
||||
late ThemeData themeData;
|
||||
|
||||
Widget get child {
|
||||
if (!videoDetailController.horizontalScreen) {
|
||||
return autoChoose(childWhenDisabled);
|
||||
}
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
if (constraints.maxWidth > constraints.maxHeight * 1.25) {
|
||||
return autoChoose(childWhenDisabledLandscape);
|
||||
} else if (constraints.maxWidth * (9 / 16) <
|
||||
(2 / 5) * constraints.maxHeight) {
|
||||
return autoChoose(childWhenDisabled);
|
||||
} else {
|
||||
return autoChoose(childWhenDisabledAlmostSquare);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
late bool isPortrait;
|
||||
late double maxWidth;
|
||||
late double maxHeight;
|
||||
late EdgeInsets padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
padding = MediaQuery.paddingOf(context);
|
||||
Widget child = LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
maxWidth = constraints.maxWidth;
|
||||
maxHeight = constraints.maxHeight;
|
||||
isPortrait = maxHeight > maxWidth;
|
||||
|
||||
if (!videoDetailController.horizontalScreen) {
|
||||
return autoChoose(childWhenDisabled);
|
||||
}
|
||||
|
||||
if (maxWidth > maxHeight * 1.25) {
|
||||
return autoChoose(childWhenDisabledLandscape);
|
||||
}
|
||||
if (maxWidth * (9 / 16) < (2 / 5) * maxHeight) {
|
||||
return autoChoose(childWhenDisabled);
|
||||
}
|
||||
return autoChoose(childWhenDisabledAlmostSquare);
|
||||
},
|
||||
);
|
||||
return videoDetailController.plPlayerController.darkVideoPage
|
||||
? Theme(data: themeData, child: child)
|
||||
: child;
|
||||
@@ -1542,19 +1563,13 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
height: 38,
|
||||
child: Obx(
|
||||
() {
|
||||
final enableShowDanmaku = videoDetailController
|
||||
.plPlayerController
|
||||
.enableShowDanmaku
|
||||
.value;
|
||||
final ctr = videoDetailController.plPlayerController;
|
||||
final enableShowDanmaku = ctr.enableShowDanmaku.value;
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
final newVal = !enableShowDanmaku;
|
||||
videoDetailController
|
||||
.plPlayerController
|
||||
.enableShowDanmaku
|
||||
.value =
|
||||
newVal;
|
||||
if (!Pref.tempPlayerConf) {
|
||||
ctr.enableShowDanmaku.value = newVal;
|
||||
if (!ctr.tempPlayerConf) {
|
||||
GStorage.setting.put(
|
||||
SettingBoxKey.enableShowDanmaku,
|
||||
newVal,
|
||||
@@ -1585,10 +1600,11 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
}
|
||||
|
||||
Widget videoPlayer(double videoWidth, double videoHeight) {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
return PopScope(
|
||||
canPop:
|
||||
!isFullScreen &&
|
||||
(videoDetailController.horizontalScreen || context.isPortrait),
|
||||
(videoDetailController.horizontalScreen || isPortrait),
|
||||
onPopInvokedWithResult: _onPopInvokedWithResult,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
@@ -1627,7 +1643,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
videoDetailController.continuePlayingPart)
|
||||
Positioned(
|
||||
left: 16,
|
||||
bottom: isFullScreen ? max(75, Get.height * 0.25) : 75,
|
||||
bottom: isFullScreen ? max(75, maxHeight * 0.25) : 75,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.textScalerOf(context).scale(120),
|
||||
child: AnimatedList(
|
||||
@@ -1746,8 +1762,13 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoIntro([bool needRelated = true, bool needCtr = true]) {
|
||||
final bottom = MediaQuery.paddingOf(context).bottom;
|
||||
Widget videoIntro({
|
||||
double? width,
|
||||
double? height,
|
||||
bool? isHorizontal,
|
||||
bool needRelated = true,
|
||||
bool needCtr = true,
|
||||
}) {
|
||||
Widget introPanel() => CustomScrollView(
|
||||
key: const PageStorageKey<String>('简介'),
|
||||
controller: needCtr ? introScrollController : null,
|
||||
@@ -1762,6 +1783,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
showAiBottomSheet: showAiBottomSheet,
|
||||
showEpisodes: showEpisodes,
|
||||
onShowMemberPage: onShowMemberPage,
|
||||
isHorizontal: isHorizontal ?? width! > height! * 1.25,
|
||||
),
|
||||
if (needRelated &&
|
||||
videoDetailController.plPlayerController.showRelatedVideo) ...[
|
||||
@@ -1779,9 +1801,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
RelatedVideoPanel(key: relatedVideoPanelKey, heroTag: heroTag),
|
||||
] else
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: bottom + StyleString.safeSpace,
|
||||
),
|
||||
child: SizedBox(height: padding.bottom + StyleString.safeSpace),
|
||||
),
|
||||
] else
|
||||
PgcIntroPage(
|
||||
@@ -1790,14 +1810,13 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
cid: videoDetailController.cid.value,
|
||||
showEpisodes: showEpisodes,
|
||||
showIntroDetail: showIntroDetail,
|
||||
maxWidth: width ?? maxWidth,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height:
|
||||
bottom +
|
||||
(videoDetailController.isPlayAll && context.isLandscape
|
||||
? 75
|
||||
: 0),
|
||||
padding.bottom +
|
||||
(videoDetailController.isPlayAll && !isPortrait ? 75 : 0),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1810,7 +1829,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
Positioned(
|
||||
left: 12,
|
||||
right: 12,
|
||||
bottom: bottom + 12,
|
||||
bottom: padding.bottom + 12,
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
@@ -2013,6 +2032,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
}
|
||||
|
||||
void showEpisodes([int? index, season, episodes, bvid, aid, cid]) {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
if (cid == null) {
|
||||
videoDetailController.showMediaListPanel(context);
|
||||
return;
|
||||
@@ -2180,7 +2200,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
if (isFullScreen) {
|
||||
plPlayerController!.triggerFullScreen(status: false);
|
||||
}
|
||||
if (!videoDetailController.horizontalScreen && context.isLandscape) {
|
||||
if (!videoDetailController.horizontalScreen && !isPortrait) {
|
||||
verticalScreenForTwoSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import 'package:PiliPlus/utils/image_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:PiliPlus/utils/video_utils.dart';
|
||||
import 'package:canvas_danmaku/canvas_danmaku.dart';
|
||||
@@ -63,7 +62,7 @@ class HeaderControl extends StatefulWidget {
|
||||
State<HeaderControl> createState() => HeaderControlState();
|
||||
}
|
||||
|
||||
class HeaderControlState extends State<HeaderControl> {
|
||||
class HeaderControlState extends TripleState<HeaderControl> {
|
||||
late final PlPlayerController plPlayerController = widget.controller;
|
||||
late final VideoDetailController videoDetailCtr = widget.videoDetailCtr;
|
||||
late final PlayUrlModel videoInfo = videoDetailCtr.data;
|
||||
@@ -72,6 +71,7 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
String get heroTag => widget.heroTag;
|
||||
late final UgcIntroController ugcIntroController;
|
||||
late final PgcIntroController pgcIntroController;
|
||||
@override
|
||||
late CommonIntroController introController = videoDetailCtr.isUgc
|
||||
? ugcIntroController
|
||||
: pgcIntroController;
|
||||
@@ -664,7 +664,7 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
..updatePlayer();
|
||||
|
||||
// update
|
||||
if (!Pref.tempPlayerConf) {
|
||||
if (!plPlayerController.tempPlayerConf) {
|
||||
final res = await Connectivity().checkConnectivity();
|
||||
if (res.contains(ConnectivityResult.wifi)) {
|
||||
setting.put(
|
||||
@@ -741,7 +741,7 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
..updatePlayer();
|
||||
|
||||
// update
|
||||
if (!Pref.tempPlayerConf) {
|
||||
if (!plPlayerController.tempPlayerConf) {
|
||||
final res = await Connectivity().checkConnectivity();
|
||||
if (res.contains(ConnectivityResult.wifi)) {
|
||||
setting.put(
|
||||
@@ -2102,7 +2102,7 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
onPressed: () {
|
||||
final newVal = !enableShowDanmaku;
|
||||
plPlayerController.enableShowDanmaku.value = newVal;
|
||||
if (!Pref.tempPlayerConf) {
|
||||
if (!plPlayerController.tempPlayerConf) {
|
||||
setting.put(SettingBoxKey.enableShowDanmaku, newVal);
|
||||
}
|
||||
},
|
||||
@@ -2228,118 +2228,112 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
],
|
||||
),
|
||||
if (showFSActionItem && isFullScreen)
|
||||
TripleBuilder(
|
||||
introController: introController,
|
||||
builder: (context, tripleAnimation, onStartTriple, onCancelTriple) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: Obx(
|
||||
() => ActionItem(
|
||||
expand: false,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.thumbsUp,
|
||||
color: Colors.white,
|
||||
),
|
||||
selectIcon: const Icon(
|
||||
FontAwesomeIcons.solidThumbsUp,
|
||||
),
|
||||
selectStatus: introController.hasLike.value,
|
||||
semanticsLabel: '点赞',
|
||||
animation: tripleAnimation,
|
||||
onStartTriple: () {
|
||||
plPlayerController.tripling = true;
|
||||
onStartTriple();
|
||||
},
|
||||
onCancelTriple: ([bool isTap = false]) {
|
||||
plPlayerController
|
||||
..tripling = false
|
||||
..hideTaskControls();
|
||||
onCancelTriple(isTap);
|
||||
},
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: Obx(
|
||||
() => ActionItem(
|
||||
expand: false,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.thumbsUp,
|
||||
color: Colors.white,
|
||||
),
|
||||
selectIcon: const Icon(
|
||||
FontAwesomeIcons.solidThumbsUp,
|
||||
),
|
||||
selectStatus: introController.hasLike.value,
|
||||
semanticsLabel: '点赞',
|
||||
animation: tripleAnimation,
|
||||
onStartTriple: () {
|
||||
plPlayerController.tripling = true;
|
||||
onStartTriple();
|
||||
},
|
||||
onCancelTriple: ([bool isTap = false]) {
|
||||
plPlayerController
|
||||
..tripling = false
|
||||
..hideTaskControls();
|
||||
onCancelTriple(isTap);
|
||||
},
|
||||
),
|
||||
if (introController case UgcIntroController ugc)
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: Obx(
|
||||
() => ActionItem(
|
||||
expand: false,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.thumbsDown,
|
||||
color: Colors.white,
|
||||
),
|
||||
selectIcon: const Icon(
|
||||
FontAwesomeIcons.solidThumbsDown,
|
||||
),
|
||||
onTap: ugc.actionDislikeVideo,
|
||||
selectStatus: ugc.hasDislike.value,
|
||||
semanticsLabel: '点踩',
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: Obx(
|
||||
() => ActionItem(
|
||||
expand: false,
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.b,
|
||||
color: Colors.white,
|
||||
),
|
||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
||||
onTap: introController.actionCoinVideo,
|
||||
selectStatus: introController.hasCoin,
|
||||
semanticsLabel: '投币',
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: Obx(
|
||||
() => ActionItem(
|
||||
expand: false,
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.star,
|
||||
color: Colors.white,
|
||||
),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||
onTap: () =>
|
||||
introController.showFavBottomSheet(context),
|
||||
onLongPress: () => introController.showFavBottomSheet(
|
||||
context,
|
||||
isLongPress: true,
|
||||
),
|
||||
selectStatus: introController.hasFav.value,
|
||||
semanticsLabel: '收藏',
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: ActionItem(
|
||||
),
|
||||
),
|
||||
if (introController case UgcIntroController ugc)
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: Obx(
|
||||
() => ActionItem(
|
||||
expand: false,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.shareFromSquare,
|
||||
FontAwesomeIcons.thumbsDown,
|
||||
color: Colors.white,
|
||||
),
|
||||
onTap: () => introController.actionShareVideo(context),
|
||||
semanticsLabel: '分享',
|
||||
selectIcon: const Icon(
|
||||
FontAwesomeIcons.solidThumbsDown,
|
||||
),
|
||||
onTap: () => ugc.handleAction(ugc.actionDislikeVideo),
|
||||
selectStatus: ugc.hasDislike.value,
|
||||
semanticsLabel: '点踩',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: Obx(
|
||||
() => ActionItem(
|
||||
expand: false,
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.b,
|
||||
color: Colors.white,
|
||||
),
|
||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
||||
onTap: introController.actionCoinVideo,
|
||||
selectStatus: introController.hasCoin,
|
||||
semanticsLabel: '投币',
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: Obx(
|
||||
() => ActionItem(
|
||||
expand: false,
|
||||
animation: tripleAnimation,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.star,
|
||||
color: Colors.white,
|
||||
),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||
onTap: () => introController.showFavBottomSheet(context),
|
||||
onLongPress: () => introController.showFavBottomSheet(
|
||||
context,
|
||||
isLongPress: true,
|
||||
),
|
||||
selectStatus: introController.hasFav.value,
|
||||
semanticsLabel: '收藏',
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 34,
|
||||
child: ActionItem(
|
||||
expand: false,
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.shareFromSquare,
|
||||
color: Colors.white,
|
||||
),
|
||||
onTap: () => introController.actionShareVideo(context),
|
||||
semanticsLabel: '分享',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -336,6 +336,8 @@ class PlPlayerController {
|
||||
late final bool pipNoDanmaku = Pref.pipNoDanmaku;
|
||||
late final bool removeSafeArea = Pref.removeSafeArea;
|
||||
|
||||
late final bool tempPlayerConf = Pref.tempPlayerConf;
|
||||
|
||||
int? cacheVideoQa;
|
||||
late int cacheAudioQa;
|
||||
bool enableHeart = true;
|
||||
@@ -656,7 +658,7 @@ class PlPlayerController {
|
||||
type ??= superResolutionType;
|
||||
} else {
|
||||
superResolutionType = type;
|
||||
if (isAnim && !Pref.tempPlayerConf) {
|
||||
if (isAnim && !tempPlayerConf) {
|
||||
GStorage.setting.put(SettingBoxKey.superResolutionType, type);
|
||||
}
|
||||
}
|
||||
@@ -1314,7 +1316,7 @@ class PlPlayerController {
|
||||
/// 设置后台播放
|
||||
Future<void> setBackgroundPlay(bool val) async {
|
||||
videoPlayerServiceHandler.enableBackgroundPlay = val;
|
||||
if (!Pref.tempPlayerConf) {
|
||||
if (!tempPlayerConf) {
|
||||
setting.put(SettingBoxKey.enableBackgroundPlay, val);
|
||||
}
|
||||
}
|
||||
@@ -1596,7 +1598,7 @@ class PlPlayerController {
|
||||
|
||||
void setContinuePlayInBackground() {
|
||||
_continuePlayInBackground.value = !_continuePlayInBackground.value;
|
||||
if (!Pref.tempPlayerConf) {
|
||||
if (!tempPlayerConf) {
|
||||
setting.put(
|
||||
SettingBoxKey.continuePlayInBackground,
|
||||
_continuePlayInBackground.value,
|
||||
|
||||
Reference in New Issue
Block a user