opt: RepaintBoundary (#1840)

* opt: RepaintBoundary

* fix [skip ci]

* opt time width

* opt: video position

* update

---------

Co-authored-by: dom <githubaccount56556@proton.me>
This commit is contained in:
My-Responsitories
2026-02-20 21:04:34 +08:00
committed by GitHub
parent a63ca93762
commit d7d9655f81
12 changed files with 226 additions and 128 deletions

View File

@@ -66,7 +66,8 @@ import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show RenderProxyBox;
import 'package:flutter/rendering.dart'
show RenderProxyBox, SemanticsConfiguration;
import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -379,38 +380,15 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
),
/// 时间进度
BottomControlType.time => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// 播放时间
Obx(
() => Text(
DurationUtils.formatDuration(
plPlayerController.positionSeconds.value,
),
style: const TextStyle(
color: Colors.white,
fontSize: 10,
height: 1.4,
fontFeatures: [FontFeature.tabularFigures()],
),
),
BottomControlType.time => Obx(
() => _VideoTime(
position: DurationUtils.formatDuration(
plPlayerController.positionSeconds.value,
),
Obx(
() => Text(
DurationUtils.formatDuration(
plPlayerController.duration.value.inSeconds,
),
style: const TextStyle(
color: Color(0xFFD0D0D0),
fontSize: 10,
height: 1.4,
fontFeatures: [FontFeature.tabularFigures()],
),
),
duration: DurationUtils.formatDuration(
plPlayerController.duration.value.inSeconds,
),
],
),
),
/// 高能进度条
@@ -1617,39 +1595,41 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
top: -1,
bottom: -1,
child: ClipRect(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppBarAni(
isTop: true,
controller: animationController,
isFullScreen: isFullScreen,
child: plPlayerController.isDesktopPip
? GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (_) => windowManager.startDragging(),
child: widget.headerControl,
)
: widget.headerControl,
),
AppBarAni(
isTop: false,
controller: animationController,
isFullScreen: isFullScreen,
child:
widget.bottomControl ??
BottomControl(
maxWidth: maxWidth,
isFullScreen: isFullScreen,
controller: plPlayerController,
videoDetailController: videoDetailController,
buildBottomControl: () => buildBottomControl(
videoDetailController,
maxWidth > maxHeight,
child: RepaintBoundary(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppBarAni(
isTop: true,
controller: animationController,
isFullScreen: isFullScreen,
child: plPlayerController.isDesktopPip
? GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (_) => windowManager.startDragging(),
child: widget.headerControl,
)
: widget.headerControl,
),
AppBarAni(
isTop: false,
controller: animationController,
isFullScreen: isFullScreen,
child:
widget.bottomControl ??
BottomControl(
maxWidth: maxWidth,
isFullScreen: isFullScreen,
controller: plPlayerController,
videoDetailController: videoDetailController,
buildBottomControl: () => buildBottomControl(
videoDetailController,
maxWidth > maxHeight,
),
),
),
),
],
),
],
),
),
),
),
@@ -2767,12 +2747,7 @@ class _RenderDanmakuTip extends RenderProxyBox {
void paint(PaintingContext context, Offset offset) {
final paint = Paint()
..color = const Color(0xB3000000)
..style = PaintingStyle.fill;
final strokePaint = Paint()
..color = const Color(0x7EFFFFFF)
..style = PaintingStyle.stroke
..strokeWidth = 1.25;
..style = .fill;
final radius = size.height / 2;
const triangleBase = _triangleHeight * 2 / 3;
@@ -2803,11 +2778,140 @@ class _RenderDanmakuTip extends RenderProxyBox {
..close();
context.canvas
..save()
..translate(offset.dx, offset.dy)
..drawPath(path, paint)
..drawPath(path, strokePaint);
..drawPath(
path,
paint
..color = const Color(0x7EFFFFFF)
..style = PaintingStyle.stroke
..strokeWidth = 1.25,
)
..restore();
super.paint(context, offset);
}
}
class _VideoTime extends LeafRenderObjectWidget {
const _VideoTime({
required this.position,
required this.duration,
});
final String position;
final String duration;
@override
_RenderVideoTime createRenderObject(BuildContext context) => _RenderVideoTime(
position: position,
duration: duration,
);
@override
void updateRenderObject(
BuildContext context,
covariant _RenderVideoTime renderObject,
) {
renderObject
..position = position
..duration = duration;
}
}
class _RenderVideoTime extends RenderBox {
_RenderVideoTime({
required String position,
required String duration,
}) : _position = position,
_duration = duration;
String _duration;
set duration(String value) {
_duration = value;
final paragraph = _buildParagraph(const Color(0xFFD0D0D0), _duration);
if (paragraph.maxIntrinsicWidth != _cache?.maxIntrinsicWidth) {
markNeedsLayout();
}
_cache?.dispose();
_cache = paragraph;
markNeedsSemanticsUpdate();
}
String _position;
set position(String value) {
_position = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
ui.Paragraph? _cache;
ui.Paragraph _buildParagraph(Color color, String time) {
final builder =
ui.ParagraphBuilder(
ui.ParagraphStyle(
fontSize: 10,
height: 1.4,
fontFamily: 'Monospace',
),
)
..pushStyle(
ui.TextStyle(
color: color,
fontSize: 10,
height: 1.4,
fontFamily: 'Monospace',
fontFeatures: const [FontFeature.tabularFigures()],
),
)
..addText(time);
return builder.build()
..layout(ui.ParagraphConstraints(width: constraints.maxWidth));
}
@override
ui.Size computeDryLayout(covariant BoxConstraints constraints) {
final paragraph = _cache ??= _buildParagraph(
const Color(0xFFD0D0D0),
_duration,
);
return Size(paragraph.maxIntrinsicWidth, paragraph.height * 2);
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
config.label = 'position:$_position\nduration:$_duration';
}
@override
void performLayout() {
size = computeDryLayout(constraints);
}
@override
void paint(PaintingContext context, ui.Offset offset) {
final para = _buildParagraph(Colors.white, _position);
context.canvas
..drawParagraph(
para,
Offset(
offset.dx + _cache!.maxIntrinsicWidth - para.maxIntrinsicWidth,
offset.dy,
),
)
..drawParagraph(_cache!, Offset(offset.dx, offset.dy + para.height));
para.dispose();
}
@override
void dispose() {
_cache?.dispose();
_cache = null;
super.dispose();
}
@override
bool get isRepaintBoundary => true;

View File

@@ -105,7 +105,7 @@ class MpvConvertWebp {
final text = log.text.toDartString().trim();
debugPrint('WebpConvert: $level $prefix : $text');
if (kDebugMode) {
_success = level != 'error' && level != 'fatal';
if (level == 'error' || level == 'fatal') _success = false;
} else {
_success = false;
}