Files
PiliPlus/lib/pages/dynamics/widgets/vote_decoration.dart
2026-07-30 12:11:18 +08:00

178 lines
4.5 KiB
Dart

import 'dart:math' as math;
import 'package:flutter/material.dart';
class VoteDecoration extends Decoration {
const VoteDecoration({
required this.color,
required this.percentage,
required this.border,
required this.borderRadius,
});
final Color color;
final double percentage;
final BoxBorder border;
final BorderRadius borderRadius;
@override
EdgeInsetsGeometry get padding => border.dimensions;
@override
Path getClipPath(Rect rect, TextDirection textDirection) {
return Path()..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is VoteDecoration &&
other.color == color &&
other.border == border &&
other.borderRadius == borderRadius &&
other.percentage == percentage;
}
@override
int get hashCode => Object.hash(color, border, borderRadius, percentage);
@override
bool hitTest(Size size, Offset position, {TextDirection? textDirection}) {
assert((Offset.zero & size).contains(position));
final Offset center = size.center(Offset.zero);
final double distance = (position - center).distance;
return distance <= math.min(size.width, size.height) / 2.0;
}
@override
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
assert(onChanged != null);
return _VoteDecorationPainter(this, onChanged);
}
}
class _VoteDecorationPainter extends BoxPainter {
_VoteDecorationPainter(this._decoration, super.onChanged);
final VoteDecoration _decoration;
Paint? _cachedBackgroundPaint;
Rect? _rectForCachedBackgroundPaint;
Paint _getBackgroundPaint(Rect rect, TextDirection? textDirection) {
assert(
_rectForCachedBackgroundPaint == null,
);
if (_cachedBackgroundPaint == null) {
final paint = Paint()..color = _decoration.color;
_cachedBackgroundPaint = paint;
}
return _cachedBackgroundPaint!;
}
void _paintBox(
Canvas canvas,
Rect rect,
Paint paint,
TextDirection? textDirection,
) {
if (_decoration.percentage == 1.0) {
canvas.drawRRect(
_decoration.borderRadius.resolve(textDirection).toRRect(rect),
paint,
);
} else {
final borderRadius = BorderRadius.horizontal(
left: _decoration.borderRadius.topLeft,
);
canvas.drawRRect(
borderRadius.resolve(textDirection).toRRect(rect),
paint,
);
}
}
void _paintBackgroundColor(
Canvas canvas,
Rect rect,
TextDirection? textDirection,
) {
final Rect adjustedRect = _adjustedRectOnOutlinedBorder(
rect,
textDirection,
);
_paintBox(
canvas,
adjustedRect,
_getBackgroundPaint(rect, textDirection),
textDirection,
);
}
double _calculateAdjustedSide(BorderSide side) {
if (side.color.alpha == 255 && side.style == .solid) {
return side.strokeInset;
}
return 0;
}
Rect _adjustedRectOnOutlinedBorder(Rect rect, TextDirection? textDirection) {
final border = _decoration.border as Border;
final EdgeInsets insets =
EdgeInsets.fromLTRB(
_calculateAdjustedSide(border.left),
_calculateAdjustedSide(border.top),
_calculateAdjustedSide(border.right),
_calculateAdjustedSide(border.bottom),
) /
2;
return Rect.fromLTRB(
rect.left + insets.left,
rect.top + insets.top,
rect.right - insets.right,
rect.bottom - insets.bottom,
);
}
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
assert(configuration.size != null);
final Rect rect = offset & configuration.size!;
final TextDirection? textDirection = configuration.textDirection;
if (_decoration.percentage > 0) {
final Rect bgRect;
if (_decoration.percentage == 1.0) {
bgRect = rect;
} else {
bgRect =
offset &
Size(
configuration.size!.width * (_decoration.percentage),
configuration.size!.height,
);
}
_paintBackgroundColor(canvas, bgRect, textDirection);
}
_decoration.border.paint(
canvas,
rect,
shape: .rectangle,
borderRadius: _decoration.borderRadius.resolve(textDirection),
textDirection: configuration.textDirection,
);
}
@override
String toString() {
return 'VotePainter for $_decoration';
}
}