opt vote option

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-07-28 19:07:13 +08:00
parent 5296a8f7f0
commit 557585334a
2 changed files with 207 additions and 38 deletions

View File

@@ -10,6 +10,7 @@ import 'package:PiliPlus/models/common/badge_type.dart';
import 'package:PiliPlus/models/common/image_preview_type.dart';
import 'package:PiliPlus/models/dynamics/vote_model.dart';
import 'package:PiliPlus/models_new/followee_votes/vote.dart';
import 'package:PiliPlus/pages/dynamics/widgets/vote_decoration.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/grid.dart';
@@ -84,7 +85,7 @@ class _VotePanelState extends State<VotePanel> {
),
Flexible(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
padding: const .symmetric(vertical: 8),
child: _voteInfo.type == 1
? GridView.builder(
shrinkWrap: true,
@@ -105,7 +106,7 @@ class _VotePanelState extends State<VotePanel> {
itemCount: _voteInfo.options.length,
itemBuilder: (context, index) => _buildOptions(index),
separatorBuilder: (context, index) =>
const SizedBox(height: 6),
const SizedBox(height: 8),
),
),
),
@@ -428,7 +429,7 @@ class _VotePanelState extends State<VotePanel> {
selected: selected,
onSelected: !_enabled
? null
: (value) => _onSelected(context, value, opt.optIdx!),
: () => _onSelected(context, !selected, opt.optIdx!),
);
},
);
@@ -464,7 +465,7 @@ class PercentageChip extends StatelessWidget {
final String label;
final double? percentage;
final bool selected;
final ValueChanged<bool>? onSelected;
final VoidCallback? onSelected;
const PercentageChip({
super.key,
@@ -474,48 +475,41 @@ class PercentageChip extends StatelessWidget {
this.percentage,
});
static final EdgeInsets _padding = PlatformUtils.isMobile
? const .symmetric(horizontal: 12, vertical: 10)
: const .symmetric(horizontal: 12, vertical: 8);
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return ChoiceChip(
tooltip: label,
labelPadding: EdgeInsets.zero,
padding: EdgeInsets.zero,
showCheckmark: false,
clipBehavior: Clip.hardEdge,
label: Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
if (percentage != null)
Positioned.fill(
left: 0,
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: percentage,
child: ColoredBox(
color: selected
? colorScheme.inversePrimary
: colorScheme.outlineVariant,
),
),
final colorScheme = ColorScheme.of(context);
return Tooltip(
message: label,
child: Material(
borderRadius: const .all(.circular(8)),
color: selected ? colorScheme.secondaryContainer : null,
child: InkWell(
onTap: onSelected,
borderRadius: const .all(.circular(8)),
child: Container(
padding: _padding,
decoration: VoteDecoration(
borderRadius: const .all(.circular(8)),
border: .all(color: colorScheme.outlineVariant),
percentage: percentage ?? 0,
color: selected
? colorScheme.inversePrimary
: colorScheme.outlineVariant,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: Row(
spacing: 8,
children: [
Expanded(
child: Row(
spacing: 4,
mainAxisSize: MainAxisSize.min,
mainAxisSize: .min,
children: [
Flexible(
child: Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
child: Text(label, maxLines: 1, overflow: .ellipsis),
),
if (selected)
Icon(
@@ -531,10 +525,8 @@ class PercentageChip extends StatelessWidget {
],
),
),
],
),
),
selected: selected,
onSelected: onSelected,
);
}
}

View File

@@ -0,0 +1,177 @@
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';
}
}