fix #1641

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-10-26 20:10:40 +08:00
parent e9dc154642
commit 032dfd69be
14 changed files with 64 additions and 115 deletions

View File

@@ -21,7 +21,6 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/custom_layout.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/marquee.dart' show ContextSingleTicker;
import 'package:PiliPlus/models/common/badge_type.dart';
import 'package:PiliPlus/models/common/image_preview_type.dart';
import 'package:PiliPlus/utils/context_ext.dart';
@@ -65,7 +64,6 @@ class CustomGridView extends StatelessWidget {
required this.maxWidth,
required this.picArr,
this.onViewImage,
this.onDismissed,
this.fullScreen = false,
});
@@ -73,7 +71,6 @@ class CustomGridView extends StatelessWidget {
final double space;
final List<ImageModel> picArr;
final VoidCallback? onViewImage;
final ValueChanged<int>? onDismissed;
final bool fullScreen;
static bool horizontalPreview = Pref.horizontalPreview;
@@ -96,20 +93,18 @@ class CustomGridView extends StatelessWidget {
!context.mediaQuerySize.isPortrait) {
final scaffoldState = Scaffold.maybeOf(context);
if (scaffoldState != null) {
onViewImage?.call();
PageUtils.onHorizontalPreviewState(
scaffoldState,
ContextSingleTicker(scaffoldState.context),
imgList,
index,
);
return;
}
}
onViewImage?.call();
PageUtils.imageView(
initialPage: index,
imgList: imgList,
onDismissed: onDismissed,
);
}

View File

@@ -8,10 +8,10 @@ import 'package:flutter/material.dart';
/// show a [Hero] animation.
class HeroDialogRoute<T> extends PageRoute<T> {
HeroDialogRoute({
required this.builder,
required this.pageBuilder,
});
final WidgetBuilder builder;
final RoutePageBuilder pageBuilder;
@override
bool get opaque => false;
@@ -50,12 +50,10 @@ class HeroDialogRoute<T> extends PageRoute<T> {
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
final Widget child = builder(context);
final Widget result = Semantics(
return Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: child,
child: pageBuilder(context, animation, secondaryAnimation),
);
return result;
}
}

View File

@@ -44,16 +44,12 @@ class InteractiveviewerGallery extends StatefulWidget {
this.itemBuilder,
this.maxScale = 8,
this.minScale = 1.0,
this.onPageChanged,
this.onDismissed,
this.onClose,
required this.quality,
this.onClose,
});
final int quality;
final ValueChanged<bool>? onClose;
/// The sources to show.
final List<SourceModel> sources;
@@ -67,9 +63,7 @@ class InteractiveviewerGallery extends StatefulWidget {
final double minScale;
final ValueChanged<int>? onPageChanged;
final ValueChanged<int>? onDismissed;
final VoidCallback? onClose;
@override
State<InteractiveviewerGallery> createState() =>
@@ -121,7 +115,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
@override
void dispose() {
widget.onClose?.call(true);
widget.onClose?.call();
_player?.dispose();
_pageController.dispose();
_animationController
@@ -206,7 +200,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
if (item.sourceType == SourceType.livePhoto) {
_onPlay(item.liveUrl!);
}
widget.onPageChanged?.call(page);
if (_transformationController.value != Matrix4.identity()) {
// animate the reset for the transformation of the interactive viewer
@@ -228,15 +221,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
: url.http2https;
}
void onClose() {
if (widget.onClose != null) {
widget.onClose!(false);
} else {
Get.back();
widget.onDismissed?.call(_pageController.page!.floor());
}
}
Player? _player;
VideoController? _videoController;
@@ -254,7 +238,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
onNoBoundaryHit: _onNoBoundaryHit,
maxScale: widget.maxScale,
minScale: widget.minScale,
onDismissed: onClose,
onDismissed: Get.back,
onReset: () {
if (!_enablePageView) {
setState(() {
@@ -277,7 +261,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
onTap: () => EasyThrottle.throttle(
'preview',
const Duration(milliseconds: 555),
onClose,
Get.back,
),
onDoubleTapDown: (TapDownDetails details) {
_doubleTapLocalPosition = details.localPosition;

View File

@@ -98,7 +98,7 @@ class VideoDetailController extends GetxController
final Rx<LoadingState> videoState = LoadingState.loading().obs;
/// 播放器配置 画质 音质 解码格式
late Rx<VideoQuality> currentVideoQa;
final Rxn<VideoQuality> currentVideoQa = Rxn<VideoQuality>();
AudioQuality? currentAudioQa;
late VideoDecodeFormatType currentDecodeFormats;
@@ -239,15 +239,7 @@ class VideoDetailController extends GetxController
}
}
bool imageStatus = false;
void onViewImage() {
imageStatus = true;
}
void onDismissed(int _) {
imageStatus = false;
}
bool imageview = false;
final isLoginVideo = Accounts.get(AccountType.video).isLogin;
@@ -993,7 +985,7 @@ class VideoDetailController extends GetxController
SmartDialog.showToast('UP主已关闭弹幕');
return;
}
final isPlaying = plPlayerController.playerStatus.playing;
final isPlaying = autoPlay.value && plPlayerController.playerStatus.playing;
if (isPlaying) {
await plPlayerController.pause();
}
@@ -1063,6 +1055,8 @@ class VideoDetailController extends GetxController
/// 更新画质、音质
void updatePlayer() {
final currentVideoQa = this.currentVideoQa.value;
if (currentVideoQa == null) return;
autoPlay.value = true;
playedTime = plPlayerController.position.value;
plPlayerController
@@ -1070,7 +1064,7 @@ class VideoDetailController extends GetxController
..isBuffering.value = false
..buffered.value = Duration.zero;
final video = findVideoByQa(currentVideoQa.value.code);
final video = findVideoByQa(currentVideoQa.code);
if (firstVideo.codecs != video.codecs) {
currentDecodeFormats = VideoDecodeFormatType.fromString(video.codecs!);
}
@@ -1089,6 +1083,15 @@ class VideoDetailController extends GetxController
playerInit();
}
FutureOr<void> _initPlayerIfNeeded() {
if ((autoPlay.value ||
(plPlayerController.preInitPlayer &&
!plPlayerController.processing)) &&
childKey.currentState?.mounted == true) {
return playerInit();
}
}
Future<void> playerInit({
String? video,
String? audio,
@@ -1248,10 +1251,8 @@ class VideoDetailController extends GetxController
);
setVideoHeight();
currentDecodeFormats = VideoDecodeFormatType.fromString('avc1');
currentVideoQa = Rx(VideoQuality.fromCode(data.quality!));
if (autoPlay.value || plPlayerController.preInitPlayer) {
await playerInit();
}
currentVideoQa.value = VideoQuality.fromCode(data.quality!);
await _initPlayerIfNeeded();
isQuerying = false;
return;
}
@@ -1282,7 +1283,7 @@ class VideoDetailController extends GetxController
numbers,
);
}
currentVideoQa = Rx(VideoQuality.fromCode(resVideoQa));
currentVideoQa.value = VideoQuality.fromCode(resVideoQa);
/// 取出符合当前画质的videoList
final List<VideoItem> videosList = allVideosList
@@ -1354,9 +1355,7 @@ class VideoDetailController extends GetxController
firstAudio = AudioItem();
audioUrl = '';
}
if (autoPlay.value || plPlayerController.preInitPlayer) {
await playerInit();
}
await _initPlayerIfNeeded();
} else {
autoPlay.value = false;
videoState.value = result..toast();

View File

@@ -145,10 +145,8 @@ class _PgcIntroPageState extends State<PgcIntroPage> {
children: [
GestureDetector(
onTap: () {
videoDetailCtr.onViewImage();
PageUtils.imageView(
imgList: [SourceModel(url: item.cover!)],
onDismissed: videoDetailCtr.onDismissed,
);
},
child: Hero(

View File

@@ -398,10 +398,8 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
Widget _buildAvatar(String face) => GestureDetector(
onTap: () {
widget.videoDetailController.onViewImage();
PageUtils.imageView(
imgList: [SourceModel(url: face)],
onDismissed: widget.videoDetailController.onDismissed,
);
},
child: NetworkImgLayer(

View File

@@ -20,15 +20,11 @@ class VideoReplyPanel extends StatefulWidget {
super.key,
this.replyLevel = 1,
required this.heroTag,
this.onViewImage,
this.onDismissed,
required this.isNested,
});
final int replyLevel;
final String heroTag;
final VoidCallback? onViewImage;
final ValueChanged<int>? onDismissed;
final bool isNested;
@override
@@ -216,8 +212,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
_videoReplyController.onRemove(index, item, subIndex),
upMid: _videoReplyController.upMid,
getTag: () => heroTag,
onViewImage: widget.onViewImage,
onDismissed: widget.onDismissed,
onCheckReply: (item) => _videoReplyController
.onCheckReply(item, isManual: true),
onToggleTop: (item) => _videoReplyController.onToggleTop(
@@ -258,8 +252,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
firstFloor: replyItem,
replyType: _videoReplyController.videoType.replyType,
isVideoDetail: true,
onViewImage: widget.onViewImage,
onDismissed: widget.onDismissed,
isNested: widget.isNested,
),
);

View File

@@ -50,7 +50,6 @@ class ReplyItemGrpc extends StatelessWidget {
this.showDialogue,
this.getTag,
this.onViewImage,
this.onDismissed,
this.onCheckReply,
this.onToggleTop,
this.jumpToDialogue,
@@ -65,7 +64,6 @@ class ReplyItemGrpc extends StatelessWidget {
final VoidCallback? showDialogue;
final Function? getTag;
final VoidCallback? onViewImage;
final ValueChanged<int>? onDismissed;
final ValueChanged<ReplyInfo>? onCheckReply;
final ValueChanged<ReplyInfo>? onToggleTop;
final VoidCallback? jumpToDialogue;
@@ -317,7 +315,6 @@ class ReplyItemGrpc extends StatelessWidget {
)
.toList(),
onViewImage: onViewImage,
onDismissed: onDismissed,
),
),
),

View File

@@ -29,8 +29,6 @@ class VideoReplyReplyPanel extends CommonSlidePage {
this.firstFloor,
required this.isVideoDetail,
required this.replyType,
this.onViewImage,
this.onDismissed,
this.isNested = false,
});
final int? id;
@@ -40,8 +38,6 @@ class VideoReplyReplyPanel extends CommonSlidePage {
final ReplyInfo? firstFloor;
final bool isVideoDetail;
final int replyType;
final VoidCallback? onViewImage;
final ValueChanged<int>? onDismissed;
final bool isNested;
@override
@@ -231,8 +227,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
index: -1,
),
upMid: _controller.upMid,
onViewImage: widget.onViewImage,
onDismissed: widget.onDismissed,
onCheckReply: (item) =>
_controller.onCheckReply(item, isManual: true),
),
@@ -390,8 +384,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
SmartDialog.showToast('评论可能已被删除');
}
},
onViewImage: widget.onViewImage,
onDismissed: widget.onDismissed,
onCheckReply: (item) => _controller.onCheckReply(item, isManual: true),
);
}

View File

@@ -6,6 +6,7 @@ import 'dart:ui';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/custom_icon.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/hero_dialog_route.dart';
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/http/loading_state.dart';
@@ -364,7 +365,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
@override
// 离开当前页面时
Future<void> didPushNext() async {
if (videoDetailController.imageStatus) {
if (Get.routing.route is HeroDialogRoute) {
videoDetailController.imageview = true;
return;
}
@@ -395,7 +397,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
@override
// 返回当前页面时
Future<void> didPopNext() async {
if (videoDetailController.imageStatus) {
if (videoDetailController.imageview) {
videoDetailController.imageview = false;
return;
}
@@ -519,7 +522,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
..addListener(animListener);
if (Utils.isMobile && mounted && isShowing && !isFullScreen) {
if (isPortrait) {
if (!videoDetailController.imageStatus) {
if (!videoDetailController.imageview) {
showStatusBar();
}
} else if (!videoDetailController.horizontalScreen) {
@@ -1944,8 +1947,6 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
key: videoReplyPanelKey,
isNested: isNested,
heroTag: heroTag,
onViewImage: videoDetailController.onViewImage,
onDismissed: videoDetailController.onDismissed,
);
// ai总结

View File

@@ -493,7 +493,7 @@ class HeaderControlState extends State<HeaderControl> {
leading: const Icon(Icons.play_circle_outline, size: 20),
title: const Text('选择画质', style: titleStyle),
subtitle: Text(
'当前画质 ${videoDetailCtr.currentVideoQa.value.desc}',
'当前画质 ${videoDetailCtr.currentVideoQa.value?.desc}',
style: subTitleStyle,
),
),
@@ -759,7 +759,8 @@ class HeaderControlState extends State<HeaderControl> {
return;
}
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
final VideoQuality currentVideoQa = videoDetailCtr.currentVideoQa.value;
final VideoQuality? currentVideoQa = videoDetailCtr.currentVideoQa.value;
if (currentVideoQa == null) return;
/// 总质量分类
final int totalQaSam = videoFormat.length;

View File

@@ -621,6 +621,9 @@ class PlPlayerController {
return _instance!;
}
bool _processing = false;
bool get processing => _processing;
// 初始化资源
Future<void> setDataSource(
DataSource dataSource, {
@@ -649,6 +652,7 @@ class PlPlayerController {
Volume? volume,
}) async {
try {
_processing = true;
this.isLive = isLive;
_videoType = videoType ?? VideoType.ugc;
this.width = width;
@@ -709,6 +713,8 @@ class PlPlayerController {
debugPrint(stackTrace.toString());
debugPrint('plPlayer err: $err');
}
} finally {
_processing = false;
}
}

View File

@@ -717,8 +717,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
BottomControlType.qa => Obx(
() {
final VideoQuality currentVideoQa =
final VideoQuality? currentVideoQa =
videoDetailController.currentVideoQa.value;
if (currentVideoQa == null) {
return const SizedBox.shrink();
}
final PlayUrlModel videoInfo = videoDetailController.data;
if (videoInfo.dash == null) {
return const SizedBox.shrink();

View File

@@ -41,17 +41,16 @@ abstract class PageUtils {
static Future<void> imageView({
int initialPage = 0,
required List<SourceModel> imgList,
ValueChanged<int>? onDismissed,
int? quality,
}) {
return Get.key.currentState!.push<void>(
HeroDialogRoute(
builder: (context) => InteractiveviewerGallery(
sources: imgList,
initIndex: initialPage,
onDismissed: onDismissed,
quality: quality ?? GlobalData().imgQuality,
),
pageBuilder: (context, animation, secondaryAnimation) =>
InteractiveviewerGallery(
sources: imgList,
initIndex: initialPage,
quality: quality ?? GlobalData().imgQuality,
),
),
);
}
@@ -555,42 +554,28 @@ abstract class PageUtils {
static void onHorizontalPreviewState(
ScaffoldState state,
TickerProvider vsync,
List<SourceModel> imgList,
int index,
) {
final ctr = AnimationController(
vsync: vsync,
duration: const Duration(milliseconds: 200),
final animController = AnimationController(
vsync: state,
duration: Duration.zero,
reverseDuration: Duration.zero,
)..forward();
state.showBottomSheet(
constraints: const BoxConstraints(),
(context) {
return FadeTransition(
opacity: Tween<double>(begin: 0, end: 1).animate(ctr),
child: InteractiveviewerGallery(
sources: imgList,
initIndex: index,
onClose: (value) async {
if (!value) {
try {
await ctr.reverse();
} catch (_) {}
}
try {
ctr.dispose();
} catch (_) {}
if (!value) {
Get.back();
}
},
quality: GlobalData().imgQuality,
),
return InteractiveviewerGallery(
sources: imgList,
initIndex: index,
quality: GlobalData().imgQuality,
onClose: animController.dispose,
);
},
enableDrag: false,
elevation: 0.0,
backgroundColor: Colors.transparent,
transitionAnimationController: animController,
sheetAnimationStyle: const AnimationStyle(duration: Duration.zero),
);
}