opt video bar

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-08-12 13:32:35 +08:00
parent be5a1af040
commit 1826b6a059
3 changed files with 193 additions and 158 deletions

View File

@@ -49,13 +49,15 @@ class ActionItem extends StatelessWidget {
clipBehavior: Clip.none, clipBehavior: Clip.none,
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
AnimatedBuilder( RepaintBoundary(
animation: animation!, child: AnimatedBuilder(
builder: (context, child) => CustomPaint( animation: animation!,
size: const Size.square(28), builder: (context, child) => CustomPaint(
painter: _ArcPainter( size: const Size.square(28),
color: primary, painter: _ArcPainter(
sweepAngle: animation!.value, color: primary,
sweepAngle: animation!.value,
),
), ),
), ),
), ),

View File

@@ -247,7 +247,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
} }
// 动态构建底部控制条 // 动态构建底部控制条
Widget buildBottomControl(bool isLandscape) { Widget buildBottomControl(bool isLandscape, double maxWidth) {
final videoDetail = introController.videoDetail.value; final videoDetail = introController.videoDetail.value;
final isSeason = videoDetail.ugcSeason != null; final isSeason = videoDetail.ugcSeason != null;
final isPart = videoDetail.pages != null && videoDetail.pages!.length > 1; final isPart = videoDetail.pages != null && videoDetail.pages!.length > 1;
@@ -659,14 +659,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
children: [ children: [
...userSpecifyItemLeft.map(progressWidget), ...userSpecifyItemLeft.map(progressWidget),
Expanded( Expanded(
child: LayoutBuilder( child: FittedBox(
builder: (context, constraints) => FittedBox( child: ConstrainedBox(
child: ConstrainedBox( constraints: BoxConstraints(minWidth: maxWidth),
constraints: BoxConstraints(minWidth: constraints.maxWidth), child: Row(
child: Row( mainAxisAlignment: MainAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end, children: userSpecifyItemRight.map(progressWidget).toList(),
children: userSpecifyItemRight.map(progressWidget).toList(),
),
), ),
), ),
), ),
@@ -1292,9 +1290,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
widget.bottomControl ?? widget.bottomControl ??
BottomControl( BottomControl(
controller: plPlayerController, controller: plPlayerController,
buildBottomControl: () => buildBottomControl: (bottomMaxWidth) =>
buildBottomControl(maxWidth > maxHeight), buildBottomControl(
maxWidth: maxWidth, maxWidth > maxHeight,
bottomMaxWidth,
),
), ),
), ),
], ],
@@ -1472,10 +1472,13 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
right: 0, right: 0,
bottom: 0.75, bottom: 0.75,
child: IgnorePointer( child: IgnorePointer(
child: CustomPaint( child: RepaintBoundary(
size: const Size(double.infinity, 3.5), child: CustomPaint(
painter: SegmentProgressBar( key: const Key('segmentList'),
segmentColors: plPlayerController.segmentList, size: const Size(double.infinity, 3.5),
painter: SegmentProgressBar(
segmentColors: plPlayerController.segmentList,
),
), ),
), ),
), ),
@@ -1487,10 +1490,13 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
right: 0, right: 0,
bottom: 0.75, bottom: 0.75,
child: IgnorePointer( child: IgnorePointer(
child: CustomPaint( child: RepaintBoundary(
size: const Size(double.infinity, 3.5), child: CustomPaint(
painter: SegmentProgressBar( key: const Key('viewPointList'),
segmentColors: plPlayerController.viewPointList, size: const Size(double.infinity, 3.5),
painter: SegmentProgressBar(
segmentColors: plPlayerController.viewPointList,
),
), ),
), ),
), ),
@@ -1851,6 +1857,10 @@ Widget buildSeekPreviewWidget(
VideoShotData data = plPlayerController.videoShot!['data']; VideoShotData data = plPlayerController.videoShot!['data'];
if (data.index.isNullOrEmpty) {
return const SizedBox.shrink();
}
try { try {
double scale = double scale =
plPlayerController.isFullScreen.value && plPlayerController.isFullScreen.value &&

View File

@@ -13,13 +13,11 @@ class BottomControl extends StatelessWidget {
const BottomControl({ const BottomControl({
required this.controller, required this.controller,
required this.buildBottomControl, required this.buildBottomControl,
required this.maxWidth,
super.key, super.key,
}); });
final PlPlayerController controller; final PlPlayerController controller;
final Widget Function() buildBottomControl; final Widget Function(double maxWidth) buildBottomControl;
final double maxWidth;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -30,136 +28,161 @@ class BottomControl extends StatelessWidget {
double lastAnnouncedValue = -1; double lastAnnouncedValue = -1;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10), padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column( child: LayoutBuilder(
mainAxisSize: MainAxisSize.min, builder: (context, constraints) {
children: [ final maxWidth = constraints.maxWidth;
Obx( return Column(
() { mainAxisSize: MainAxisSize.min,
final int value = controller.sliderPositionSeconds.value; children: [
final int max = controller.durationSeconds.value.inSeconds; Obx(
final int buffer = controller.bufferedSeconds.value; () {
if (value > max || max <= 0) { final int value = controller.sliderPositionSeconds.value;
return const SizedBox.shrink(); final int max = controller.durationSeconds.value.inSeconds;
} final int buffer = controller.bufferedSeconds.value;
return Padding( if (value > max || max <= 0) {
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 7), return const SizedBox.shrink();
child: Semantics( }
value: '${(value / max * 100).round()}%', return Padding(
child: Stack( padding: const EdgeInsets.only(
clipBehavior: Clip.none, left: 10,
alignment: Alignment.bottomCenter, right: 10,
children: [ bottom: 7,
if (controller.dmTrend.isNotEmpty && ),
controller.showDmTreandChart.value) child: Semantics(
buildDmChart(theme, controller, 4.5), value: '${(value / max * 100).round()}%',
if (controller.viewPointList.isNotEmpty && child: Stack(
controller.showVP.value) clipBehavior: Clip.none,
buildViewPointWidget(controller, 8.75, maxWidth), alignment: Alignment.bottomCenter,
ProgressBar( children: [
progress: Duration(seconds: value), if (controller.dmTrend.isNotEmpty &&
buffered: Duration(seconds: buffer), controller.showDmTreandChart.value)
total: Duration(seconds: max), buildDmChart(theme, controller, 4.5),
progressBarColor: colorTheme, if (controller.viewPointList.isNotEmpty &&
baseBarColor: Colors.white.withValues(alpha: 0.2), controller.showVP.value)
bufferedBarColor: colorTheme.withValues(alpha: 0.4), buildViewPointWidget(
timeLabelLocation: TimeLabelLocation.none, controller,
thumbColor: colorTheme, 8.75,
barHeight: 3.5, maxWidth - 20,
thumbRadius: 7, ),
onDragStart: (duration) { ProgressBar(
feedBack(); progress: Duration(seconds: value),
controller.onChangedSliderStart(duration.timeStamp); buffered: Duration(seconds: buffer),
}, total: Duration(seconds: max),
onDragUpdate: (duration) { progressBarColor: colorTheme,
double newProgress = baseBarColor: Colors.white.withValues(alpha: 0.2),
duration.timeStamp.inSeconds / max; bufferedBarColor: colorTheme.withValues(alpha: 0.4),
if (controller.showSeekPreview) { timeLabelLocation: TimeLabelLocation.none,
if (!controller.showPreview.value) { thumbColor: colorTheme,
controller.showPreview.value = true; barHeight: 3.5,
} thumbRadius: 7,
controller.previewDx.value = onDragStart: (duration) {
duration.localPosition.dx; feedBack();
} controller.onChangedSliderStart(
if ((newProgress - lastAnnouncedValue).abs() > 0.02) { duration.timeStamp,
accessibilityDebounce?.cancel(); );
accessibilityDebounce = Timer( },
const Duration(milliseconds: 200), onDragUpdate: (duration) {
() { double newProgress =
SemanticsService.announce( duration.timeStamp.inSeconds / max;
"${(newProgress * 100).round()}%", if (controller.showSeekPreview) {
TextDirection.ltr, if (!controller.showPreview.value) {
controller.showPreview.value = true;
}
controller.previewDx.value =
duration.localPosition.dx;
}
if ((newProgress - lastAnnouncedValue).abs() >
0.02) {
accessibilityDebounce?.cancel();
accessibilityDebounce = Timer(
const Duration(milliseconds: 200),
() {
SemanticsService.announce(
"${(newProgress * 100).round()}%",
TextDirection.ltr,
);
lastAnnouncedValue = newProgress;
},
); );
lastAnnouncedValue = newProgress; }
}, controller.onUpdatedSliderProgress(
); duration.timeStamp,
} );
controller.onUpdatedSliderProgress( },
duration.timeStamp, onSeek: (duration) {
); if (controller.showSeekPreview) {
}, controller.showPreview.value = false;
onSeek: (duration) { }
if (controller.showSeekPreview) { controller
controller.showPreview.value = false; ..onChangedSliderEnd()
} ..onChangedSlider(duration.inSeconds.toDouble())
controller ..seekTo(
..onChangedSliderEnd() Duration(seconds: duration.inSeconds),
..onChangedSlider(duration.inSeconds.toDouble()) isSeek: false,
..seekTo( );
Duration(seconds: duration.inSeconds), SemanticsService.announce(
isSeek: false, "${(duration.inSeconds / max * 100).round()}%",
); TextDirection.ltr,
SemanticsService.announce( );
"${(duration.inSeconds / max * 100).round()}%", },
TextDirection.ltr, ),
); if (controller.segmentList.isNotEmpty)
}, Positioned(
left: 0,
right: 0,
bottom: 5.25,
child: IgnorePointer(
child: RepaintBoundary(
child: CustomPaint(
key: const Key('segmentList'),
size: const Size(double.infinity, 3.5),
painter: SegmentProgressBar(
segmentColors: controller.segmentList,
),
),
),
),
),
if (controller.viewPointList.isNotEmpty &&
controller.showVP.value)
Positioned(
left: 0,
right: 0,
bottom: 5.25,
child: IgnorePointer(
child: RepaintBoundary(
child: CustomPaint(
key: const Key('viewPointList'),
size: const Size(double.infinity, 3.5),
painter: SegmentProgressBar(
segmentColors: controller.viewPointList,
),
),
),
),
),
if (controller.showSeekPreview &&
controller.showControls.value)
Positioned(
left: 0,
right: 0,
bottom: 18,
child: buildSeekPreviewWidget(
controller,
maxWidth - 20,
),
),
],
), ),
if (controller.segmentList.isNotEmpty) ),
Positioned( );
left: 0, },
right: 0, ),
bottom: 5.25, buildBottomControl(maxWidth),
child: IgnorePointer( const SizedBox(height: 12),
child: CustomPaint( ],
size: const Size(double.infinity, 3.5), );
painter: SegmentProgressBar( },
segmentColors: controller.segmentList,
),
),
),
),
if (controller.viewPointList.isNotEmpty &&
controller.showVP.value)
Positioned(
left: 0,
right: 0,
bottom: 5.25,
child: IgnorePointer(
child: CustomPaint(
size: const Size(double.infinity, 3.5),
painter: SegmentProgressBar(
segmentColors: controller.viewPointList,
),
),
),
),
if (controller.showSeekPreview &&
controller.showControls.value)
Positioned(
left: 0,
right: 0,
bottom: 18,
child: buildSeekPreviewWidget(controller, maxWidth),
),
],
),
),
);
},
),
buildBottomControl(),
const SizedBox(height: 12),
],
), ),
); );
} }