refa custom painter

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-01-18 22:21:48 +08:00
parent a3ddc83430
commit f5657d2d4c
9 changed files with 586 additions and 275 deletions

View File

@@ -0,0 +1,163 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
import 'dart:ui' as ui;
import 'package:flutter/widgets.dart';
class CroppedImage extends LeafRenderObjectWidget {
const CroppedImage({
super.key,
required this.size,
required this.image,
required this.srcRect,
required this.dstRect,
required this.rrect,
required this.imgPaint,
required this.borderPaint,
});
final Size size;
final ui.Image image;
final Rect srcRect;
final Rect dstRect;
final RRect rrect;
final Paint imgPaint;
final Paint borderPaint;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderCroppedImage(
preferredSize: size,
image: image,
srcRect: srcRect,
dstRect: dstRect,
rrect: rrect,
imgPaint: imgPaint,
borderPaint: borderPaint,
);
}
@override
void updateRenderObject(
BuildContext context,
RenderCroppedImage renderObject,
) {
renderObject
..preferredSize = size
..image = image
..srcRect = srcRect
..dstRect = dstRect
..rrect = rrect
..imgPaint = imgPaint
..borderPaint = borderPaint;
}
}
class RenderCroppedImage extends RenderBox {
RenderCroppedImage({
required Size preferredSize,
required ui.Image image,
required Rect srcRect,
required Rect dstRect,
required RRect rrect,
required Paint imgPaint,
required Paint borderPaint,
}) : _preferredSize = preferredSize,
_image = image,
_srcRect = srcRect,
_dstRect = dstRect,
_rrect = rrect,
_imgPaint = imgPaint,
_borderPaint = borderPaint;
Size _preferredSize;
Size get preferredSize => _preferredSize;
set preferredSize(Size value) {
if (_preferredSize == value) return;
_preferredSize = value;
markNeedsLayout();
}
ui.Image _image;
ui.Image get image => _image;
set image(ui.Image value) {
if (_image == value) return;
_image = value;
markNeedsPaint();
}
Rect _srcRect;
Rect get srcRect => _srcRect;
set srcRect(Rect value) {
if (_srcRect == value) return;
_srcRect = value;
markNeedsPaint();
}
Rect _dstRect;
Rect get dstRect => _dstRect;
set dstRect(Rect value) {
if (_dstRect == value) return;
_dstRect = value;
markNeedsPaint();
}
RRect _rrect;
RRect get rrect => _rrect;
set rrect(RRect value) {
if (_rrect == value) return;
_rrect = value;
markNeedsPaint();
}
Paint _imgPaint;
Paint get imgPaint => _imgPaint;
set imgPaint(Paint value) {
if (_imgPaint == value) return;
_imgPaint = value;
markNeedsPaint();
}
Paint _borderPaint;
Paint get borderPaint => _borderPaint;
set borderPaint(Paint value) {
if (_borderPaint == value) return;
_borderPaint = value;
markNeedsPaint();
}
@override
void performLayout() {
size = computeDryLayout(constraints);
}
@override
Size computeDryLayout(BoxConstraints constraints) {
return constraints.constrain(_preferredSize);
}
@override
void paint(PaintingContext context, Offset offset) {
context.canvas
..drawImageRect(image, srcRect, dstRect, _imgPaint)
..drawRRect(rrect, _borderPaint);
}
@override
bool get isRepaintBoundary => true;
}

View File

@@ -0,0 +1,117 @@
import 'dart:math' show pi;
import 'package:flutter/widgets.dart';
class Arc extends LeafRenderObjectWidget {
const Arc({
super.key,
required this.size,
required this.color,
required this.sweepAngle,
this.strokeWidth = 2,
});
final double size;
final Color color;
final double sweepAngle;
final double strokeWidth;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderArc(
size: size,
color: color,
sweepAngle: sweepAngle,
strokeWidth: strokeWidth,
);
}
@override
void updateRenderObject(
BuildContext context,
RenderArc renderObject,
) {
renderObject
..color = color
..sweepAngle = sweepAngle
..strokeWidth = strokeWidth;
}
}
class RenderArc extends RenderBox {
RenderArc({
required double size,
required Color color,
required double sweepAngle,
required double strokeWidth,
}) : _preferredSize = Size.square(size),
_color = color,
_sweepAngle = sweepAngle,
_strokeWidth = strokeWidth;
Color _color;
Color get color => _color;
set color(Color value) {
if (_color == value) return;
_color = value;
markNeedsPaint();
}
double _sweepAngle;
double get sweepAngle => _sweepAngle;
set sweepAngle(double value) {
if (_sweepAngle == value) return;
_sweepAngle = value;
markNeedsPaint();
}
double _strokeWidth;
double get strokeWidth => _strokeWidth;
set strokeWidth(double value) {
if (_strokeWidth == value) return;
_strokeWidth = value;
markNeedsPaint();
}
Size _preferredSize;
set preferredSize(Size value) {
if (_preferredSize == value) return;
_preferredSize = value;
markNeedsLayout();
}
@override
void performLayout() {
size = computeDryLayout(constraints);
}
@override
Size computeDryLayout(BoxConstraints constraints) {
return constraints.constrain(_preferredSize);
}
@override
void paint(PaintingContext context, Offset offset) {
if (sweepAngle == 0) {
return;
}
final paint = Paint()
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
final size = this.size;
final rect = Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2),
radius: size.width / 2,
);
const startAngle = -pi / 2;
context.canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
}
@override
bool get isRepaintBoundary => true;
}

View File

@@ -3,7 +3,7 @@ import 'dart:ui' show clampDouble;
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
enum TooltipType { top, right }
@@ -261,19 +261,83 @@ class _CustomMultiTooltipPositionDelegate extends MultiChildLayoutDelegate {
}
}
class TrianglePainter extends CustomPainter {
TrianglePainter(this.color, {this.type = TooltipType.top});
final TooltipType type;
class Triangle extends LeafRenderObjectWidget {
const Triangle({
super.key,
required this.color,
required this.size,
this.type = .top,
});
final Color color;
final Size size;
final TooltipType type;
@override
void paint(Canvas canvas, Size size) {
RenderObject createRenderObject(BuildContext context) {
return RenderTriangle(
color: color,
size: size,
type: type,
);
}
@override
void updateRenderObject(
BuildContext context,
RenderTriangle renderObject,
) {
renderObject
..color = color
..preferredSize = size;
}
}
class RenderTriangle extends RenderBox {
RenderTriangle({
required Color color,
required Size size,
required TooltipType type,
}) : _color = color,
_preferredSize = size,
_type = type;
Color _color;
Color get color => _color;
set color(Color value) {
if (_color == value) return;
_color = value;
markNeedsPaint();
}
Size _preferredSize;
set preferredSize(Size value) {
if (_preferredSize == value) return;
_preferredSize = value;
markNeedsLayout();
}
final TooltipType _type;
@override
void performLayout() {
size = computeDryLayout(constraints);
}
@override
Size computeDryLayout(BoxConstraints constraints) {
return constraints.constrain(_preferredSize);
}
@override
void paint(PaintingContext context, Offset offset) {
final size = this.size;
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
Path path;
switch (type) {
switch (_type) {
case TooltipType.top:
path = Path()
..moveTo(0, 0)
@@ -288,11 +352,11 @@ class TrianglePainter extends CustomPainter {
..close();
}
canvas.drawPath(path, paint);
context.canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(TrianglePainter oldDelegate) => color != oldDelegate.color;
bool get isRepaintBoundary => true;
}
Offset positionDependentBox({

View File

@@ -4,10 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class DisabledIcon<T extends Widget> extends SingleChildRenderObjectWidget {
final Color? color;
final double lineLengthScale;
final StrokeCap strokeCap;
const DisabledIcon({
super.key,
required T child,
@@ -18,27 +14,70 @@ class DisabledIcon<T extends Widget> extends SingleChildRenderObjectWidget {
strokeCap = strokeCap ?? StrokeCap.butt,
super(child: child);
final Color? color;
final StrokeCap strokeCap;
final double lineLengthScale;
T enable() => child as T;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderMaskedIcon(
color ??
color:
color ??
(child is Icon
? (child as Icon).color ?? IconTheme.of(context).color!
: IconTheme.of(context).color!),
lineLengthScale,
strokeCap,
strokeCap: strokeCap,
lineLengthScale: lineLengthScale,
);
}
T enable() => child as T;
@override
void updateRenderObject(BuildContext context, RenderMaskedIcon renderObject) {
renderObject
..color =
color ??
(child is Icon
? (child as Icon).color ?? IconTheme.of(context).color!
: IconTheme.of(context).color!)
..strokeCap = strokeCap
..lineLengthScale = lineLengthScale;
}
}
class RenderMaskedIcon extends RenderProxyBox {
final Color color;
final double lineLengthScale;
final StrokeCap strokeCap;
RenderMaskedIcon({
required Color color,
required StrokeCap strokeCap,
required double lineLengthScale,
}) : _color = color,
_strokeCap = strokeCap,
_lineLengthScale = lineLengthScale;
RenderMaskedIcon(this.color, this.lineLengthScale, this.strokeCap);
Color _color;
Color get color => _color;
set color(Color value) {
if (_color == value) return;
_color = value;
markNeedsPaint();
}
StrokeCap _strokeCap;
StrokeCap get strokeCap => _strokeCap;
set strokeCap(StrokeCap value) {
if (_strokeCap == value) return;
_strokeCap = value;
markNeedsPaint();
}
double _lineLengthScale;
double get lineLengthScale => _lineLengthScale;
set lineLengthScale(double value) {
if (_lineLengthScale == value) return;
_lineLengthScale = value;
markNeedsPaint();
}
@override
void paint(PaintingContext context, Offset offset) {
@@ -77,7 +116,7 @@ class RenderMaskedIcon extends RenderProxyBox {
..clipPath(path, doAntiAlias: false);
super.paint(context, offset);
context.canvas.restore();
canvas.restore();
final linePaint = Paint()
..color = color
@@ -94,6 +133,9 @@ class RenderMaskedIcon extends RenderProxyBox {
linePaint,
);
}
@override
bool get isRepaintBoundary => true;
}
extension DisabledIconExt on Icon {

View File

@@ -1,6 +1,6 @@
import 'dart:math';
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_item.dart';
import 'package:PiliPlus/common/widgets/custom_arc.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -30,18 +30,13 @@ class LoadingWidget extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
//loading animation
RepaintBoundary.wrap(
Obx(
() => CustomPaint(
size: const Size.square(40),
painter: ArcPainter(
color: onSurfaceVariant,
strokeWidth: 3,
sweepAngle: progress.value * 2 * pi,
),
),
Obx(
() => Arc(
size: 40,
color: onSurfaceVariant,
strokeWidth: 3,
sweepAngle: progress.value * 2 * pi,
),
0,
),
//msg
Text(msg, style: TextStyle(color: onSurfaceVariant)),