mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
mod: 详情页横屏布局(bug很多不要下载!)
This commit is contained in:
@@ -2,7 +2,6 @@ import 'package:flutter/material.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:hive/hive.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/common/widgets/badge.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
@@ -15,7 +14,6 @@ import 'package:pilipala/pages/video/detail/introduction/widgets/action_item.dar
|
||||
import 'package:pilipala/pages/video/detail/introduction/widgets/action_row_item.dart';
|
||||
import 'package:pilipala/pages/video/detail/introduction/widgets/fav_panel.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
import 'widgets/intro_detail.dart';
|
||||
@@ -116,9 +114,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
||||
String heroTag = Get.arguments['heroTag'];
|
||||
late final BangumiIntroController bangumiIntroController;
|
||||
late final VideoDetailController videoDetailCtr;
|
||||
Box localCache = GStrorage.localCache;
|
||||
late final BangumiInfoModel? bangumiItem;
|
||||
late double sheetHeight;
|
||||
int? cid;
|
||||
bool isProcessing = false;
|
||||
void Function()? handleState(Future Function() action) {
|
||||
@@ -137,7 +133,6 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
||||
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
|
||||
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
||||
bangumiItem = bangumiIntroController.bangumiItem;
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
cid = widget.cid!;
|
||||
print('cid: $cid');
|
||||
videoDetailCtr.cid.listen((p0) {
|
||||
@@ -369,7 +364,6 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
||||
(bangumiItem != null
|
||||
? bangumiItem!.episodes!.first.cid
|
||||
: widget.bangumiDetail!.episodes!.first.cid),
|
||||
sheetHeight: sheetHeight,
|
||||
changeFuc: (bvid, cid, aid) => bangumiIntroController
|
||||
.changeSeasonOrbangu(bvid, cid, aid),
|
||||
)
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.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/storage.dart';
|
||||
|
||||
Box localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
|
||||
class IntroDetail extends StatelessWidget {
|
||||
final dynamic bangumiDetail;
|
||||
@@ -17,7 +14,6 @@ class IntroDetail extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
TextStyle smallTitle = TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
@@ -25,7 +21,7 @@ class IntroDetail extends StatelessWidget {
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||
height: sheetHeight,
|
||||
height: context.height.abs() * 0.7,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
|
||||
@@ -12,13 +12,11 @@ class BangumiPanel extends StatefulWidget {
|
||||
super.key,
|
||||
required this.pages,
|
||||
this.cid,
|
||||
this.sheetHeight,
|
||||
this.changeFuc,
|
||||
});
|
||||
|
||||
final List<EpisodeItem> pages;
|
||||
final int? cid;
|
||||
final double? sheetHeight;
|
||||
final Function? changeFuc;
|
||||
|
||||
@override
|
||||
@@ -80,7 +78,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
||||
});
|
||||
// 在这里使用 setState 更新状态
|
||||
return Container(
|
||||
height: widget.sheetHeight,
|
||||
height: context.height.abs() * 0.7,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
@@ -89,10 +89,6 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
||||
Widget build(BuildContext context) {
|
||||
Box localCache = GStrorage.localCache;
|
||||
double statusBarHeight = MediaQuery.of(context).padding.top;
|
||||
double sheetHeight = MediaQuery.sizeOf(context).height -
|
||||
MediaQuery.of(context).padding.top -
|
||||
MediaQuery.sizeOf(context).width * 9 / 16;
|
||||
localCache.put('sheetHeight', sheetHeight);
|
||||
localCache.put('statusBarHeight', statusBarHeight);
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
|
||||
@@ -241,6 +241,10 @@ class VideoDetailController extends GetxController
|
||||
plPlayerController.headerControl = headerControl;
|
||||
}
|
||||
|
||||
void setTriggerFullScreenCallback(void Function({bool? status}) callback) {
|
||||
plPlayerController.setTriggerFullscreenCallback(callback);
|
||||
}
|
||||
|
||||
// 视频链接
|
||||
Future queryVideoUrl() async {
|
||||
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
||||
@@ -359,7 +363,7 @@ class VideoDetailController extends GetxController
|
||||
if (result['code'] == -404) {
|
||||
isShowCover.value = false;
|
||||
}
|
||||
if (result['code'] == 87008){
|
||||
if (result['code'] == 87008) {
|
||||
SmartDialog.showToast("当前视频可能是专属视频,可能需包月充电观看(${result['msg']})");
|
||||
} else {
|
||||
SmartDialog.showToast("错误(${result['code']}):${result['msg']}");
|
||||
|
||||
@@ -123,9 +123,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
late final VideoDetailController videoDetailCtr;
|
||||
late final Map<dynamic, dynamic> videoItem;
|
||||
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
final Box<dynamic> setting = GStrorage.setting;
|
||||
late double sheetHeight;
|
||||
|
||||
late final bool loadingStatus; // 加载状态
|
||||
|
||||
@@ -153,7 +151,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
videoIntroController = Get.put(VideoIntroController(), tag: heroTag);
|
||||
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
||||
videoItem = videoIntroController.videoItem!;
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
|
||||
loadingStatus = widget.loadingStatus;
|
||||
owner = loadingStatus ? videoItem['owner'] : widget.videoDetail!.owner;
|
||||
@@ -381,7 +378,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
cid: videoIntroController.lastPlayCid.value != 0
|
||||
? videoIntroController.lastPlayCid.value
|
||||
: widget.videoDetail!.pages!.first.cid,
|
||||
sheetHeight: sheetHeight,
|
||||
changeFuc: (bvid, cid, aid) => videoIntroController
|
||||
.changeSeasonOrbangu(bvid, cid, aid),
|
||||
),
|
||||
@@ -393,7 +389,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
Obx(() => PagesPanel(
|
||||
pages: widget.videoDetail!.pages!,
|
||||
cid: videoIntroController.lastPlayCid.value,
|
||||
sheetHeight: sheetHeight,
|
||||
changeFuc: (cid) =>
|
||||
videoIntroController.changeSeasonOrbangu(
|
||||
videoIntroController.bvid, cid, null),
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class FavPanel extends StatefulWidget {
|
||||
const FavPanel({super.key, this.ctr});
|
||||
@@ -14,21 +12,18 @@ class FavPanel extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FavPanelState extends State<FavPanel> {
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
late Future _futureBuilderFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
_futureBuilderFuture = widget.ctr!.queryVideoInFolder();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: sheetHeight,
|
||||
height: context.height.abs() * 0.7,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/http/member.dart';
|
||||
import 'package:pilipala/models/member/tags.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class GroupPanel extends StatefulWidget {
|
||||
final int? mid;
|
||||
@@ -17,8 +15,6 @@ class GroupPanel extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GroupPanelState extends State<GroupPanel> {
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
late Future _futureBuilderFuture;
|
||||
late List<MemberTagItemModel> tagsList;
|
||||
bool showDefault = true;
|
||||
@@ -26,7 +22,6 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
_futureBuilderFuture = MemberHttp.followUpTags();
|
||||
}
|
||||
|
||||
@@ -56,7 +51,7 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: sheetHeight,
|
||||
height: context.height.abs() * 0.7,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
|
||||
@@ -2,15 +2,10 @@ import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
Box localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
|
||||
class IntroDetail extends StatelessWidget {
|
||||
const IntroDetail({
|
||||
super.key,
|
||||
@@ -20,11 +15,10 @@ class IntroDetail extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||
height: sheetHeight,
|
||||
height: context.height.abs() * 0.7,
|
||||
child: Column(
|
||||
children: [
|
||||
InkWell(
|
||||
|
||||
@@ -8,12 +8,10 @@ class PagesPanel extends StatefulWidget {
|
||||
super.key,
|
||||
required this.pages,
|
||||
this.cid,
|
||||
this.sheetHeight,
|
||||
this.changeFuc,
|
||||
});
|
||||
final List<Part> pages;
|
||||
final int? cid;
|
||||
final double? sheetHeight;
|
||||
final Function? changeFuc;
|
||||
|
||||
@override
|
||||
@@ -96,7 +94,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
_scrollController.jumpTo(currentIndex * 56);
|
||||
});
|
||||
return Container(
|
||||
height: widget.sheetHeight,
|
||||
height: context.height.abs() * 0.7,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
@@ -10,12 +10,10 @@ class SeasonPanel extends StatefulWidget {
|
||||
super.key,
|
||||
required this.ugcSeason,
|
||||
this.cid,
|
||||
this.sheetHeight,
|
||||
this.changeFuc,
|
||||
});
|
||||
final UgcSeason ugcSeason;
|
||||
final int? cid;
|
||||
final double? sheetHeight;
|
||||
final Function? changeFuc;
|
||||
|
||||
@override
|
||||
@@ -104,7 +102,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
itemScrollController.jumpTo(index: currentIndex);
|
||||
});
|
||||
return Container(
|
||||
height: widget.sheetHeight,
|
||||
height: context.height.abs() * 0.7,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/common/skeleton/video_reply.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/models/common/reply_type.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
|
||||
@@ -35,8 +33,6 @@ class VideoReplyReplyPanel extends StatefulWidget {
|
||||
class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
late VideoReplyReplyController _videoReplyReplyController;
|
||||
late AnimationController replyAnimationCtl;
|
||||
final Box<dynamic> localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
Future? _futureBuilderFuture;
|
||||
late ScrollController scrollController;
|
||||
|
||||
@@ -61,7 +57,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
},
|
||||
);
|
||||
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
_futureBuilderFuture = _videoReplyReplyController.queryReplyList();
|
||||
}
|
||||
|
||||
@@ -76,7 +71,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: widget.source == 'videoDetail' ? sheetHeight : null,
|
||||
height: widget.source == 'videoDetail' ? context.height.abs() * 0.7 : null,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
@@ -63,6 +63,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
// 生命周期监听
|
||||
late final AppLifecycleListener _lifecycleListener;
|
||||
bool isShowing = true;
|
||||
bool isFullScreen = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -91,7 +92,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
autoPlayEnable =
|
||||
setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true);
|
||||
autoPiP = setting.get(SettingBoxKey.autoPiP, defaultValue: false);
|
||||
|
||||
videoDetailController
|
||||
.setTriggerFullScreenCallback(triggerFullScreenCallback);
|
||||
videoSourceInit();
|
||||
appbarStreamListen();
|
||||
lifecycleListener();
|
||||
@@ -163,6 +165,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
|
||||
/// 未开启自动播放时触发播放
|
||||
Future<void> handlePlay() async {
|
||||
videoDetailController
|
||||
.setTriggerFullScreenCallback(triggerFullScreenCallback);
|
||||
await videoDetailController.playerInit();
|
||||
plPlayerController = videoDetailController.plPlayerController;
|
||||
plPlayerController!.addStatusLister(playerListener);
|
||||
@@ -231,6 +235,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
setState(() => isShowing = true);
|
||||
videoDetailController.isFirstTime = false;
|
||||
final bool autoplay = autoPlayEnable;
|
||||
videoDetailController
|
||||
.setTriggerFullScreenCallback(triggerFullScreenCallback);
|
||||
videoDetailController.playerInit(autoplay: autoplay);
|
||||
|
||||
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
|
||||
@@ -279,6 +285,13 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
}
|
||||
}
|
||||
|
||||
void triggerFullScreenCallback({bool? status}) {
|
||||
SmartDialog.showToast('triggerFullScreen $status $isFullScreen');
|
||||
setState(() {
|
||||
isFullScreen = status ?? !isFullScreen;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16;
|
||||
@@ -286,11 +299,11 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
statusBarHeight + kToolbarHeight + videoHeight;
|
||||
Widget childWhenDisabled = SafeArea(
|
||||
top: MediaQuery.of(context).orientation == Orientation.portrait &&
|
||||
plPlayerController?.isFullScreen.value == true,
|
||||
isFullScreen == true,
|
||||
bottom: MediaQuery.of(context).orientation == Orientation.portrait &&
|
||||
plPlayerController?.isFullScreen.value == true,
|
||||
left: false, //plPlayerController?.isFullScreen.value != true,
|
||||
right: false, //plPlayerController?.isFullScreen.value != true,
|
||||
isFullScreen == true,
|
||||
left: false, //isFullScreen != true,
|
||||
right: false, //isFullScreen != true,
|
||||
child: Stack(
|
||||
children: [
|
||||
Scaffold(
|
||||
@@ -313,7 +326,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
() {
|
||||
if (MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape ||
|
||||
plPlayerController?.isFullScreen.value == true) {
|
||||
isFullScreen == true) {
|
||||
enterFullScreen();
|
||||
} else {
|
||||
exitFullScreen();
|
||||
@@ -329,7 +342,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
expandedHeight: MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape ||
|
||||
plPlayerController?.isFullScreen.value == true
|
||||
isFullScreen == true
|
||||
? MediaQuery.sizeOf(context).height -
|
||||
(MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape
|
||||
@@ -339,11 +352,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
backgroundColor: Colors.black,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: PopScope(
|
||||
canPop: plPlayerController?.isFullScreen.value !=
|
||||
true,
|
||||
canPop: isFullScreen != true,
|
||||
onPopInvoked: (bool didPop) {
|
||||
if (plPlayerController?.isFullScreen.value ==
|
||||
true) {
|
||||
if (isFullScreen == true) {
|
||||
plPlayerController!
|
||||
.triggerFullScreen(status: false);
|
||||
}
|
||||
@@ -501,9 +512,12 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
// },
|
||||
/// 不收回
|
||||
pinnedHeaderSliverHeightBuilder: () {
|
||||
if (playerStatus != PlayerStatus.playing) {
|
||||
return 0;
|
||||
}
|
||||
return MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape ||
|
||||
plPlayerController?.isFullScreen.value == true
|
||||
isFullScreen == true
|
||||
? MediaQuery.sizeOf(context).height
|
||||
: pinnedHeaderHeight;
|
||||
},
|
||||
@@ -603,6 +617,213 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget childWhenDisabledLandscape = SafeArea(
|
||||
left: isFullScreen != true,
|
||||
right: isFullScreen != true,
|
||||
child: Stack(children: [
|
||||
Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
key: videoDetailController.scaffoldKey,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(0),
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
),
|
||||
),
|
||||
body: Row(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: isFullScreen == true
|
||||
? Get.width
|
||||
: Get.height * 0.6 * 16 / 9,
|
||||
height: isFullScreen == true
|
||||
? Get.height
|
||||
: Get.height * 0.6,
|
||||
child: PopScope(
|
||||
canPop: isFullScreen != true,
|
||||
onPopInvoked: (bool didPop) {
|
||||
if (isFullScreen == true) {
|
||||
plPlayerController!
|
||||
.triggerFullScreen(status: false);
|
||||
}
|
||||
if (MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape &&
|
||||
exitFullscreenAutoVertical) {
|
||||
verticalScreen();
|
||||
}
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context,
|
||||
BoxConstraints boxConstraints) {
|
||||
final double maxWidth =
|
||||
boxConstraints.maxWidth / 2;
|
||||
final double maxHeight =
|
||||
boxConstraints.maxHeight / 2;
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
if (isShowing)
|
||||
Obx(
|
||||
() => !videoDetailController
|
||||
.autoPlay.value ||
|
||||
plPlayerController
|
||||
?.videoController ==
|
||||
null
|
||||
? nil
|
||||
: PLVideoPlayer(
|
||||
controller:
|
||||
plPlayerController!,
|
||||
headerControl:
|
||||
videoDetailController
|
||||
.headerControl,
|
||||
danmuWidget: Obx(
|
||||
() => PlDanmaku(
|
||||
key: Key(
|
||||
videoDetailController
|
||||
.danmakuCid.value
|
||||
.toString()),
|
||||
cid: videoDetailController
|
||||
.danmakuCid.value,
|
||||
playerController:
|
||||
plPlayerController!,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// 关闭自动播放时 手动播放
|
||||
if (!videoDetailController
|
||||
.autoPlay.value) ...<Widget>[
|
||||
Obx(
|
||||
() => Visibility(
|
||||
visible: videoDetailController
|
||||
.isShowCover.value,
|
||||
child: Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
handlePlay();
|
||||
},
|
||||
child: NetworkImgLayer(
|
||||
type: 'emote',
|
||||
src: videoDetailController
|
||||
.videoItem['pic'],
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => Visibility(
|
||||
visible: videoDetailController
|
||||
.isShowCover.value &&
|
||||
videoDetailController
|
||||
.isEffective.value,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: AppBar(
|
||||
primary: false,
|
||||
foregroundColor:
|
||||
Colors.white,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
backgroundColor:
|
||||
Colors.transparent,
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: '稍后再看',
|
||||
onPressed: () async {
|
||||
var res = await UserHttp
|
||||
.toViewLater(
|
||||
bvid: videoDetailController
|
||||
.bvid);
|
||||
SmartDialog
|
||||
.showToast(
|
||||
res['msg']);
|
||||
},
|
||||
icon: const Icon(Icons
|
||||
.history_outlined),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14)
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 12,
|
||||
bottom: 10,
|
||||
child: IconButton(
|
||||
tooltip: '播放',
|
||||
onPressed: () =>
|
||||
handlePlay(),
|
||||
icon: Image.asset(
|
||||
'assets/images/play.png',
|
||||
width: 60,
|
||||
height: 60,
|
||||
)),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
]
|
||||
],
|
||||
);
|
||||
},
|
||||
))),
|
||||
SizedBox(
|
||||
width: isFullScreen == true
|
||||
? Get.width
|
||||
: Get.height * 0.6 * 16 / 9,
|
||||
height: isFullScreen == true ? 0 : Get.height * 0.4,
|
||||
child: (videoDetailController.videoType ==
|
||||
SearchType.video)
|
||||
? const CustomScrollView(
|
||||
slivers: [VideoIntroPanel()])
|
||||
: (videoDetailController.videoType ==
|
||||
SearchType.media_bangumi)
|
||||
? Obx(() => BangumiIntroPanel(
|
||||
cid: videoDetailController.cid.value))
|
||||
: const SizedBox(),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
width: isFullScreen == true
|
||||
? 0
|
||||
: (Get.width -
|
||||
MediaQuery.of(context).padding.left -
|
||||
MediaQuery.of(context).padding.right -
|
||||
Get.height * 0.6 * 16 / 9),
|
||||
height: Get.height,
|
||||
child: TabBarView(
|
||||
controller: videoDetailController.tabCtr,
|
||||
children: <Widget>[
|
||||
CustomScrollView(
|
||||
slivers: [
|
||||
RelatedVideoPanel(),
|
||||
],
|
||||
),
|
||||
VideoReplyPanel(
|
||||
bvid: videoDetailController.bvid,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
))
|
||||
]));
|
||||
Widget childWhenEnabled = FutureBuilder(
|
||||
key: Key(heroTag),
|
||||
future: _futureBuilderFuture,
|
||||
@@ -632,14 +853,25 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
}
|
||||
},
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
return PiPSwitcher(
|
||||
childWhenDisabled: childWhenDisabled,
|
||||
childWhenEnabled: childWhenEnabled,
|
||||
floating: floating,
|
||||
);
|
||||
} else {
|
||||
return childWhenDisabled;
|
||||
}
|
||||
return OrientationBuilder(
|
||||
builder: (BuildContext context, Orientation orientation) {
|
||||
print("orientation:$orientation");
|
||||
if (orientation == Orientation.portrait || isFullScreen == true) {
|
||||
// 竖屏
|
||||
return childWhenDisabled;
|
||||
} else {
|
||||
enterFullScreen();
|
||||
return childWhenDisabledLandscape;
|
||||
}
|
||||
});
|
||||
// if (Platform.isAndroid) {
|
||||
// return PiPSwitcher(
|
||||
// childWhenDisabled: childWhenDisabled,
|
||||
// childWhenEnabled: childWhenEnabled,
|
||||
// floating: floating,
|
||||
// );
|
||||
// } else {
|
||||
// return childWhenDisabled;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,10 @@ import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/models/video/ai.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
Box localCache = GStrorage.localCache;
|
||||
late double sheetHeight;
|
||||
|
||||
class AiDetail extends StatelessWidget {
|
||||
final ModelResult? modelResult;
|
||||
@@ -21,11 +17,10 @@ class AiDetail extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||
height: sheetHeight,
|
||||
height: context.height.abs() * 0.7,
|
||||
child: Column(
|
||||
children: [
|
||||
InkWell(
|
||||
|
||||
@@ -31,6 +31,7 @@ Box localCache = GStrorage.localCache;
|
||||
class PlPlayerController {
|
||||
Player? _videoPlayerController;
|
||||
VideoController? _videoController;
|
||||
void Function({bool? status})? triggerFullscreenCallback;
|
||||
|
||||
// 添加一个私有静态变量来保存实例
|
||||
static PlPlayerController? _instance;
|
||||
@@ -232,6 +233,11 @@ class PlPlayerController {
|
||||
// 播放顺序相关
|
||||
PlayRepeat playRepeat = PlayRepeat.pause;
|
||||
|
||||
void setTriggerFullscreenCallback(
|
||||
void Function({bool? status}) triggerFullscreenCallback) {
|
||||
this.triggerFullscreenCallback = triggerFullscreenCallback;
|
||||
}
|
||||
|
||||
void updateSliderPositionSecond() {
|
||||
int newSecond = _sliderPosition.value.inSeconds;
|
||||
if (sliderPositionSeconds.value != newSecond) {
|
||||
@@ -1001,6 +1007,9 @@ class PlPlayerController {
|
||||
}
|
||||
toggleFullScreen(false);
|
||||
}
|
||||
if (triggerFullscreenCallback != null) {
|
||||
triggerFullscreenCallback!(status: status);
|
||||
}
|
||||
}
|
||||
|
||||
void addPositionListener(Function(Duration position) listener) =>
|
||||
|
||||
@@ -48,6 +48,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
late VideoController videoController;
|
||||
final PLVideoPlayerController _ctr = Get.put(PLVideoPlayerController());
|
||||
|
||||
final GlobalKey _playerKey = GlobalKey();
|
||||
// bool _mountSeekBackwardButton = false;
|
||||
// bool _mountSeekForwardButton = false;
|
||||
// bool _hideSeekBackwardButton = false;
|
||||
@@ -72,7 +73,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
late int defaultBtmProgressBehavior;
|
||||
late bool enableQuickDouble;
|
||||
late bool enableBackgroundPlay;
|
||||
late double screenWidth;
|
||||
|
||||
// 用于记录上一次全屏切换手势触发时间,避免误触
|
||||
DateTime? lastFullScreenToggleTime;
|
||||
@@ -116,7 +116,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
screenWidth = Get.size.width;
|
||||
animationController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 300));
|
||||
videoController = widget.controller.videoController!;
|
||||
@@ -210,6 +209,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
);
|
||||
return Stack(
|
||||
fit: StackFit.passthrough,
|
||||
key: _playerKey,
|
||||
children: <Widget>[
|
||||
Obx(
|
||||
() => Video(
|
||||
@@ -445,7 +445,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
||||
return;
|
||||
}
|
||||
final double totalWidth = MediaQuery.sizeOf(context).width;
|
||||
RenderBox renderBox = _playerKey.currentContext!.findRenderObject() as RenderBox;
|
||||
final double totalWidth = renderBox.size.width;
|
||||
final double tapPosition = details.localPosition.dx;
|
||||
final double sectionWidth = totalWidth / 3;
|
||||
String type = 'left';
|
||||
@@ -475,7 +476,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
// final double tapPosition = details.localPosition.dx;
|
||||
final int curSliderPosition =
|
||||
_.sliderPosition.value.inMilliseconds;
|
||||
final double scale = 90000 / MediaQuery.sizeOf(context).width;
|
||||
RenderBox renderBox = _playerKey.currentContext!.findRenderObject() as RenderBox;
|
||||
final double scale = 90000 / renderBox.size.width;
|
||||
final Duration pos = Duration(
|
||||
milliseconds:
|
||||
curSliderPosition + (details.delta.dx * scale).round());
|
||||
@@ -494,7 +496,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
},
|
||||
// 垂直方向 音量/亮度调节
|
||||
onVerticalDragUpdate: (DragUpdateDetails details) async {
|
||||
final double totalWidth = MediaQuery.sizeOf(context).width;
|
||||
RenderBox renderBox = _playerKey.currentContext!.findRenderObject() as RenderBox;
|
||||
final double totalWidth = renderBox.size.width;
|
||||
final double tapPosition = details.localPosition.dx;
|
||||
final double sectionWidth = totalWidth / 3;
|
||||
final double delta = details.delta.dy;
|
||||
@@ -510,10 +513,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}
|
||||
if (tapPosition < sectionWidth) {
|
||||
// 左边区域 👈
|
||||
final double level = (_.isFullScreen.value
|
||||
? Get.size.height
|
||||
: screenWidth * 9 / 16) *
|
||||
3;
|
||||
final double level = renderBox.size.height * 3;
|
||||
final double brightness =
|
||||
_ctr.brightnessValue.value - delta / level;
|
||||
final double result = brightness.clamp(0.0, 1.0);
|
||||
@@ -540,10 +540,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
_distance = dy;
|
||||
} else {
|
||||
// 右边区域 👈
|
||||
final double level = (_.isFullScreen.value
|
||||
? Get.size.height
|
||||
: screenWidth * 9 / 16) *
|
||||
0.5;
|
||||
final double level = renderBox.size.height * 0.5;
|
||||
if(lastVolume < 0) {
|
||||
lastVolume = _ctr.volumeValue.value;
|
||||
}
|
||||
@@ -761,11 +758,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
)
|
||||
: nil,
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width / 4,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// Expanded(
|
||||
// child: SizedBox(
|
||||
// width: context.width / 4,
|
||||
// ),
|
||||
// ),
|
||||
Expanded(
|
||||
child: _ctr.mountSeekForwardButton.value
|
||||
? TweenAnimationBuilder<double>(
|
||||
|
||||
@@ -146,7 +146,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
fuc: () => triggerFullScreen!(),
|
||||
fuc: () => triggerFullScreen!(status: !_.isFullScreen.value),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user