diff --git a/lib/common/widgets/progress_bar/audio_video_progress_bar.dart b/lib/common/widgets/progress_bar/audio_video_progress_bar.dart index abdf56f12..faadf8182 100644 --- a/lib/common/widgets/progress_bar/audio_video_progress_bar.dart +++ b/lib/common/widgets/progress_bar/audio_video_progress_bar.dart @@ -365,12 +365,15 @@ class RenderProgressBar extends RenderBox { _thumbGlowColor = thumbGlowColor, _thumbGlowRadius = thumbGlowRadius, _paintThumbGlow = thumbGlowRadius > thumbRadius, - _thumbCanPaintOutsideBar = thumbCanPaintOutsideBar { - _drag = _EagerHorizontalDragGestureRecognizer() - ..onStart = _onDragStart - ..onUpdate = _onDragUpdate - ..onEnd = _onDragEnd - ..onCancel = _finishDrag; + _thumbCanPaintOutsideBar = thumbCanPaintOutsideBar, + _hitTestSelf = onDragStart != null { + if (onDragStart != null) { + _drag = _EagerHorizontalDragGestureRecognizer() + ..onStart = _onDragStart + ..onUpdate = _onDragUpdate + ..onEnd = _onDragEnd + ..onCancel = _finishDrag; + } if (!_userIsDraggingThumb) { _progress = progress; _thumbValue = _proportionOfTotal(_progress); @@ -380,6 +383,7 @@ class RenderProgressBar extends RenderBox { @override void dispose() { _drag?.dispose(); + _drag = null; super.dispose(); } @@ -659,8 +663,9 @@ class RenderProgressBar extends RenderBox { @override double computeMaxIntrinsicHeight(double width) => _heightWhenNoLabels(); + final bool _hitTestSelf; @override - bool hitTestSelf(Offset position) => true; + bool hitTestSelf(Offset position) => _hitTestSelf; @override void handleEvent(PointerEvent event, BoxHitTestEntry entry) { diff --git a/lib/common/widgets/progress_bar/segment_progress_bar.dart b/lib/common/widgets/progress_bar/segment_progress_bar.dart index 84bfdc8d2..bf66e0ae8 100644 --- a/lib/common/widgets/progress_bar/segment_progress_bar.dart +++ b/lib/common/widgets/progress_bar/segment_progress_bar.dart @@ -1,23 +1,43 @@ -import 'package:flutter/material.dart'; +/* + * This file is part of PiliPlus + * + * PiliPlus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PiliPlus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PiliPlus. If not, see . + */ -class Segment { +import 'package:flutter/foundation.dart' show listEquals, kDebugMode; +import 'package:flutter/gestures.dart' show TapGestureRecognizer; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart' show BoxHitTestEntry; + +sealed class BaseSegment { final double start; final double end; - final Color color; - final String? title; - final String? url; - final int? from; - final int? to; - Segment( - this.start, - this.end, - this.color, [ - this.title, - this.url, - this.from, - this.to, - ]); + BaseSegment({ + required this.start, + required this.end, + }); +} + +class Segment extends BaseSegment { + final Color color; + + Segment({ + required super.start, + required super.end, + required this.color, + }); @override bool operator ==(Object other) { @@ -25,9 +45,38 @@ class Segment { return true; } if (other is Segment) { + return start == other.start && end == other.end && color == other.color; + } + return false; + } + + @override + int get hashCode => Object.hash(start, end, color); +} + +class ViewPointSegment extends BaseSegment { + final String? title; + final String? url; + final int? from; + final int? to; + + ViewPointSegment({ + required super.start, + required super.end, + this.title, + this.url, + this.from, + this.to, + }); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is ViewPointSegment) { return start == other.start && end == other.end && - color == other.color && title == other.title && url == other.url && from == other.from && @@ -37,116 +86,284 @@ class Segment { } @override - int get hashCode => Object.hash(start, end, color, title, url, from, to); + int get hashCode => Object.hash(start, end, title, url, from, to); } -class SegmentProgressBar extends CustomPainter { - final List segmentColors; - double? _defHeight; - - SegmentProgressBar({ - required this.segmentColors, +class SegmentProgressBar extends BaseSegmentProgressBar { + const SegmentProgressBar({ + super.key, + super.height = 3.5, + required super.segments, }); @override - void paint(Canvas canvas, Size size) { + RenderObject createRenderObject(BuildContext context) { + return RenderProgressBar( + height: height, + segments: segments, + ); + } +} + +class RenderProgressBar extends BaseRenderProgressBar { + RenderProgressBar({ + required super.height, + required super.segments, + }); + + @override + void paint(PaintingContext context, Offset offset) { + final size = this.size; + final canvas = context.canvas; final paint = Paint()..style = PaintingStyle.fill; - for (int i = 0; i < segmentColors.length; i++) { - final item = segmentColors[i]; - paint.color = item.color; + for (final segment in segments) { + paint.color = segment.color; + final segmentStart = segment.start * size.width; + final segmentEnd = segment.end * size.width; + + if (segmentEnd > segmentStart || + (segmentEnd == segmentStart && segmentStart > 0)) { + canvas.drawRect( + Rect.fromLTWH( + segmentStart, + 0, + segmentEnd == segmentStart ? 2 : segmentEnd - segmentStart, + size.height, + ), + paint, + ); + } + } + } +} + +class ViewPointSegmentProgressBar + extends BaseSegmentProgressBar { + const ViewPointSegmentProgressBar({ + super.key, + super.height = 3.5, + required super.segments, + this.onSeek, + }); + + final ValueSetter? onSeek; + + @override + RenderObject createRenderObject(BuildContext context) { + return RenderViewPointProgressBar( + height: height, + segments: segments, + onSeek: onSeek, + ); + } + + @override + void updateRenderObject( + BuildContext context, + RenderViewPointProgressBar renderObject, + ) { + renderObject + ..height = height + ..segments = segments + ..onSeek = onSeek; + } +} + +class RenderViewPointProgressBar + extends BaseRenderProgressBar { + RenderViewPointProgressBar({ + required super.height, + required super.segments, + ValueSetter? onSeek, + }) : _onSeek = onSeek, + _hitTestSelf = onSeek != null { + if (onSeek != null) { + _tapGestureRecognizer = TapGestureRecognizer()..onTapUp = _onTapUp; + } + } + + static const double barHeight = 15.0; + + @override + Size computeDryLayout(BoxConstraints constraints) { + return constraints.constrain(Size(constraints.maxWidth, barHeight)); + } + + @override + void paint(PaintingContext context, Offset offset) { + final size = this.size; + final canvas = context.canvas; + final paint = Paint()..style = PaintingStyle.fill; + + canvas.drawRect( + Rect.fromLTWH(0, 0, size.width, barHeight), + paint..color = Colors.grey[600]!.withValues(alpha: 0.45), + ); + + paint.color = Colors.black.withValues(alpha: 0.5); + + for (int index = 0; index < segments.length; index++) { + final isFirst = index == 0; + final item = segments[index]; final segmentStart = item.start * size.width; final segmentEnd = item.end * size.width; if (segmentEnd > segmentStart || (segmentEnd == segmentStart && segmentStart > 0)) { - if (item.title != null) { - double fontSize = 10; + double fontSize = 10; - _defHeight ??= - (TextPainter( - text: TextSpan( - text: item.title, - style: TextStyle( - fontSize: fontSize, - ), - ), - textDirection: TextDirection.ltr, - )..layout()).height + - 2; - - TextPainter getTextPainter() => TextPainter( - text: TextSpan( - text: item.title, - style: TextStyle( - color: Colors.white, - fontSize: fontSize, - height: 1, - ), + TextPainter getTextPainter() => TextPainter( + text: TextSpan( + text: item.title, + style: TextStyle( + color: Colors.white, + fontSize: fontSize, + height: 1, ), - strutStyle: StrutStyle(leading: 0, height: 1, fontSize: fontSize), - textDirection: TextDirection.ltr, - )..layout(); + ), + strutStyle: StrutStyle(leading: 0, height: 1, fontSize: fontSize), + textDirection: TextDirection.ltr, + )..layout(); - TextPainter textPainter = getTextPainter(); + TextPainter textPainter = getTextPainter(); - late double prevStart; - if (i != 0) { - prevStart = segmentColors[i - 1].start * size.width; - } - double width = i == 0 ? segmentStart : segmentStart - prevStart; - - while (textPainter.width > width - 2 && fontSize >= 2) { - fontSize -= 0.5; - textPainter = getTextPainter(); - } - - if (i == 0) { - canvas.drawRect( - Rect.fromLTRB( - 0, - -_defHeight!, - size.width, - 0, - ), - Paint()..color = Colors.grey[600]!.withValues(alpha: 0.45), - ); - } - - canvas.drawRect( - Rect.fromLTWH( - segmentStart, - -_defHeight!, - segmentEnd == segmentStart ? 2 : segmentEnd - segmentStart, - size.height + _defHeight!, - ), - paint, - ); - - double textX = i == 0 - ? (segmentStart - textPainter.width) / 2 - : (segmentStart - prevStart - textPainter.width) / 2 + - prevStart + - 1; - double textY = (-_defHeight! - textPainter.height) / 2; - textPainter.paint(canvas, Offset(textX, textY)); - } else { - canvas.drawRect( - Rect.fromLTWH( - segmentStart, - 0, - segmentEnd == segmentStart ? 2 : segmentEnd - segmentStart, - size.height, - ), - paint, - ); + late double prevStart; + if (!isFirst) { + prevStart = segments[index - 1].start * size.width; } + final width = isFirst ? segmentStart : segmentStart - prevStart; + + while (textPainter.width > width - 2 && fontSize >= 2) { + fontSize -= 0.5; + textPainter.dispose(); + textPainter = getTextPainter(); + } + + canvas.drawRect( + Rect.fromLTWH( + segmentStart, + 0, + segmentEnd == segmentStart ? 2 : segmentEnd - segmentStart, + barHeight + height, + ), + paint, + ); + + final textX = isFirst + ? (segmentStart - textPainter.width) / 2 + : (segmentStart - prevStart - textPainter.width) / 2 + + prevStart + + 1; + final textY = (barHeight - textPainter.height) / 2; + textPainter.paint(canvas, Offset(textX, textY)); } } } + ValueSetter? _onSeek; + set onSeek(ValueSetter? value) { + if (_onSeek == value) { + return; + } + _onSeek = value; + } + + TapGestureRecognizer? _tapGestureRecognizer; + @override - bool shouldRepaint(SegmentProgressBar oldDelegate) { - return segmentColors != oldDelegate.segmentColors; + void dispose() { + _onSeek = null; + _tapGestureRecognizer + ?..onTapUp = null + ..dispose(); + _tapGestureRecognizer = null; + super.dispose(); + } + + final bool _hitTestSelf; + @override + bool hitTestSelf(Offset position) => _hitTestSelf; + + @override + void handleEvent(PointerEvent event, BoxHitTestEntry entry) { + if (event is PointerDownEvent) { + _tapGestureRecognizer?.addPointer(event); + } + } + + void _onTapUp(TapUpDetails details) { + try { + final seg = details.localPosition.dx / size.width; + final item = _segments + .where((item) => item.start >= seg) + .reduce((a, b) => a.start < b.start ? a : b); + if (item.from case final from?) { + _onSeek?.call(Duration(seconds: from)); + } + // if (kDebugMode) debugPrint('${item.title},,${item.from}'); + } catch (e) { + if (kDebugMode) rethrow; + } } } + +abstract class BaseSegmentProgressBar + extends LeafRenderObjectWidget { + const BaseSegmentProgressBar({ + super.key, + this.height = 3.5, + required this.segments, + }); + + final double height; + final List segments; + + @override + void updateRenderObject( + BuildContext context, + BaseRenderProgressBar renderObject, + ) { + renderObject + ..height = height + ..segments = segments; + } +} + +class BaseRenderProgressBar extends RenderBox { + BaseRenderProgressBar({ + required double height, + required List segments, + ValueSetter? onSeek, + }) : _height = height, + _segments = segments; + + double _height; + double get height => _height; + set height(double value) { + if (_height == value) return; + _height = value; + markNeedsLayout(); + } + + List _segments; + List get segments => _segments; + set segments(List value) { + if (listEquals(_segments, value)) return; + _segments = value; + markNeedsPaint(); + } + + @override + void performLayout() { + size = computeDryLayout(constraints); + } + + @override + Size computeDryLayout(BoxConstraints constraints) { + return constraints.constrain(Size(constraints.maxWidth, height)); + } + + @override + bool get isRepaintBoundary => true; +} diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index 3f33cdff8..fd3f66557 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -838,7 +838,11 @@ class VideoDetailController extends GetxController segmentList.map((e) { double start = (e.segment.first / duration).clamp(0.0, 1.0); double end = (e.segment.second / duration).clamp(0.0, 1.0); - return Segment(start, end, _getColor(e.segmentType)); + return Segment( + start: start, + end: end, + color: _getColor(e.segmentType), + ); }), ); @@ -1453,7 +1457,7 @@ class VideoDetailController extends GetxController late final Map vttSubtitles = {}; late final RxInt vttSubtitlesIndex = (-1).obs; late final RxBool showVP = true.obs; - late final RxList viewPointList = [].obs; + late final RxList viewPointList = [].obs; // 设定字幕轨道 Future setSubtitle(int index) async { @@ -1575,14 +1579,13 @@ class VideoDetailController extends GetxController 0.0, 1.0, ); - return Segment( - start, - start, - Colors.black.withValues(alpha: 0.5), - item.content, - item.imgUrl, - item.from, - item.to, + return ViewPointSegment( + start: start, + end: start, + title: item.content, + url: item.imgUrl, + from: item.from, + to: item.to, ); }).toList(); } catch (_) {} diff --git a/lib/pages/video/view_point/view.dart b/lib/pages/video/view_point/view.dart index d669f85ee..d5d65fc5e 100644 --- a/lib/pages/video/view_point/view.dart +++ b/lib/pages/video/view_point/view.dart @@ -100,7 +100,7 @@ class _ViewPointsPageState extends State ), itemCount: videoDetailController.viewPointList.length, itemBuilder: (context, index) { - Segment segment = videoDetailController.viewPointList[index]; + final segment = videoDetailController.viewPointList[index]; if (currentIndex == -1 && segment.from != null && segment.to != null) { final positionSeconds = videoDetailController.plPlayerController.positionSeconds.value; @@ -122,7 +122,7 @@ class _ViewPointsPageState extends State return child; } - Widget _buildItem(ThemeData theme, Segment segment, bool isCurr) { + Widget _buildItem(ThemeData theme, ViewPointSegment segment, bool isCurr) { final theme = Theme.of(context); return Material( type: MaterialType.transparency, diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index a5b1900ad..b7fe8ff8d 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -1740,106 +1740,69 @@ class _PLVideoPlayerState extends State right: 0, child: Obx( () { - if (plPlayerController.showControls.value) { - return const SizedBox.shrink(); - } - - switch (plPlayerController.progressType) { - case BtmProgressBehavior.onlyShowFullScreen: - if (!isFullScreen) { - return const SizedBox.shrink(); - } - case BtmProgressBehavior.onlyHideFullScreen: - if (isFullScreen) { - return const SizedBox.shrink(); - } - default: - } - - return Stack( - clipBehavior: Clip.none, - alignment: Alignment.bottomCenter, - children: [ - IgnorePointer( - child: RepaintBoundary.wrap( - Obx(() { - final int value = - plPlayerController.sliderPositionSeconds.value; - final int max = plPlayerController - .durationSeconds - .value - .inSeconds; - final int buffer = - plPlayerController.bufferedSeconds.value; - return ProgressBar( - progress: Duration(seconds: value), - buffered: Duration(seconds: buffer), - total: Duration(seconds: max), - progressBarColor: primary, - baseBarColor: const Color(0x33FFFFFF), - bufferedBarColor: bufferedBarColor, - thumbColor: primary, - thumbGlowColor: thumbGlowColor, - barHeight: 3.5, - thumbRadius: 2.5, - ); - }), - 0, - ), - ), - if (plPlayerController.enableBlock && - videoDetailController.segmentProgressList.isNotEmpty) - Positioned( - left: 0, - right: 0, - bottom: 0.75, - child: IgnorePointer( - child: RepaintBoundary.wrap( - CustomPaint( - size: const Size(double.infinity, 3.5), - painter: SegmentProgressBar( - segmentColors: - videoDetailController.segmentProgressList, - ), - ), - 1, + final offstage = switch (plPlayerController.progressType) { + BtmProgressBehavior.onlyShowFullScreen => !isFullScreen, + BtmProgressBehavior.onlyHideFullScreen => isFullScreen, + _ => plPlayerController.showControls.value, + }; + return Offstage( + offstage: offstage, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.bottomCenter, + children: [ + Obx(() { + final int value = + plPlayerController.sliderPositionSeconds.value; + final int max = + plPlayerController.durationSeconds.value.inSeconds; + final int buffer = + plPlayerController.bufferedSeconds.value; + return ProgressBar( + progress: Duration(seconds: value), + buffered: Duration(seconds: buffer), + total: Duration(seconds: max), + progressBarColor: primary, + baseBarColor: const Color(0x33FFFFFF), + bufferedBarColor: bufferedBarColor, + thumbColor: primary, + thumbGlowColor: thumbGlowColor, + barHeight: 3.5, + thumbRadius: 2.5, + ); + }), + if (plPlayerController.enableBlock && + videoDetailController.segmentProgressList.isNotEmpty) + Positioned( + left: 0, + right: 0, + bottom: 0.75, + child: SegmentProgressBar( + segments: videoDetailController.segmentProgressList, ), ), - ), - if (plPlayerController.showViewPoints && - videoDetailController.viewPointList.isNotEmpty && - videoDetailController.showVP.value) ...[ - Positioned( - left: 0, - right: 0, - bottom: 0.75, - child: IgnorePointer( - child: RepaintBoundary.wrap( - CustomPaint( - size: const Size(double.infinity, 3.5), - painter: SegmentProgressBar( - segmentColors: - videoDetailController.viewPointList, - ), - ), - 2, + if (plPlayerController.showViewPoints && + videoDetailController.viewPointList.isNotEmpty && + videoDetailController.showVP.value) + Padding( + padding: const .only(bottom: 4.25), + child: ViewPointSegmentProgressBar( + segments: videoDetailController.viewPointList, + onSeek: PlatformUtils.isMobile + ? (position) => plPlayerController.seekTo( + position, + isSeek: false, + ) + : null, ), ), - ), - if (PlatformUtils.isMobile) - buildViewPointWidget( - videoDetailController, - plPlayerController, - 4.25, - maxWidth, - ), + if (plPlayerController.showDmChart && + videoDetailController.showDmTrendChart.value) + if (videoDetailController.dmTrend.value?.dataOrNull + case final list?) + buildDmChart(primary, list, videoDetailController), ], - if (plPlayerController.showDmChart && - videoDetailController.showDmTrendChart.value) - if (videoDetailController.dmTrend.value?.dataOrNull - case final list?) - buildDmChart(primary, list, videoDetailController), - ], + ), ); }, ), @@ -2325,7 +2288,7 @@ class _PLVideoPlayerState extends State // fullscreen if (dx > maxWidth) { _removeDmAction(); - return const Positioned(left: 0, top: 0, child: SizedBox.shrink()); + return const SizedBox.shrink(); } final seekOffset = _getValidOffset(item.content.text); @@ -2346,7 +2309,7 @@ class _PLVideoPlayerState extends State if (right > (maxWidth - item.xPosition)) { _removeDmAction(); - return const Positioned(left: 0, top: 0, child: SizedBox.shrink()); + return const SizedBox.shrink(); } final extra = item.content.extra; diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index f5b0fbc5a..f58851767 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -22,9 +22,31 @@ class BottomControl extends StatelessWidget { final double maxWidth; final bool isFullScreen; final PlPlayerController controller; - final Widget Function() buildBottomControl; + final ValueGetter buildBottomControl; final VideoDetailController videoDetailController; + void onDragStart(ThumbDragDetails duration) { + feedBack(); + controller.onChangedSliderStart(duration.timeStamp); + } + + void onDragUpdate(ThumbDragDetails duration, int max) { + if (!controller.isFileSource && controller.showSeekPreview) { + controller.updatePreviewIndex(duration.timeStamp.inSeconds); + } + controller.onUpdatedSliderProgress(duration.timeStamp); + } + + void onSeek(Duration duration, int max) { + if (controller.showSeekPreview) { + controller.showPreview.value = false; + } + controller + ..onChangedSliderEnd() + ..onChangedSlider(duration.inSeconds.toDouble()) + ..seekTo(Duration(seconds: duration.inSeconds), isSeek: false); + } + @override Widget build(BuildContext context) { final colorScheme = ColorScheme.of(context); @@ -33,27 +55,6 @@ class BottomControl extends StatelessWidget { : colorScheme.primary; final thumbGlowColor = primary.withAlpha(80); final bufferedBarColor = primary.withValues(alpha: 0.4); - void onDragStart(ThumbDragDetails duration) { - feedBack(); - controller.onChangedSliderStart(duration.timeStamp); - } - - void onDragUpdate(ThumbDragDetails duration, int max) { - if (!controller.isFileSource && controller.showSeekPreview) { - controller.updatePreviewIndex(duration.timeStamp.inSeconds); - } - controller.onUpdatedSliderProgress(duration.timeStamp); - } - - void onSeek(Duration duration, int max) { - if (controller.showSeekPreview) { - controller.showPreview.value = false; - } - controller - ..onChangedSliderEnd() - ..onChangedSlider(duration.inSeconds.toDouble()) - ..seekTo(Duration(seconds: duration.inSeconds), isSeek: false); - } Widget progressBar() { final child = Obx(() { @@ -93,63 +94,42 @@ class BottomControl extends StatelessWidget { Padding( padding: const EdgeInsets.fromLTRB(10, 0, 10, 7), child: Obx( - () => Stack( - clipBehavior: Clip.none, - alignment: Alignment.bottomCenter, - children: [ - progressBar(), - if (controller.enableBlock && - videoDetailController.segmentProgressList.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: - videoDetailController.segmentProgressList, - ), - ), + () => Offstage( + offstage: !controller.showControls.value, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.bottomCenter, + children: [ + progressBar(), + if (controller.enableBlock && + videoDetailController.segmentProgressList.isNotEmpty) + Positioned( + left: 0, + right: 0, + bottom: 5.25, + child: SegmentProgressBar( + segments: videoDetailController.segmentProgressList, ), ), - ), - if (controller.showViewPoints && - videoDetailController.viewPointList.isNotEmpty && - videoDetailController.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: - videoDetailController.viewPointList, - ), - ), + if (controller.showViewPoints && + videoDetailController.viewPointList.isNotEmpty && + videoDetailController.showVP.value) + Padding( + padding: const .only(bottom: 8.75), + child: ViewPointSegmentProgressBar( + segments: videoDetailController.viewPointList, + onSeek: PlatformUtils.isDesktop + ? (position) => + controller.seekTo(position, isSeek: false) + : null, ), ), - ), - if (!PlatformUtils.isMobile) - buildViewPointWidget( - videoDetailController, - controller, - 8.75, - maxWidth - 40, - ), + if (videoDetailController.showDmTrendChart.value) + if (videoDetailController.dmTrend.value?.dataOrNull + case final list?) + buildDmChart(primary, list, videoDetailController, 4.5), ], - if (videoDetailController.showDmTrendChart.value) - if (videoDetailController.dmTrend.value?.dataOrNull - case final list?) - buildDmChart(primary, list, videoDetailController, 4.5), - ], + ), ), ), ),