fix:靠原型不齐页面

This commit is contained in:
jingyun
2026-06-18 16:53:08 +08:00
parent 35eed6b5b8
commit f42ad566e0
14 changed files with 4013 additions and 569 deletions
+292
View File
@@ -0,0 +1,292 @@
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
class PrototypeFrame extends StatelessWidget {
const PrototypeFrame({
super.key,
required this.child,
this.top = 0,
this.bottom = 24,
});
final Widget child;
final double top;
final double bottom;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.fromLTRB(18, top, 18, bottom),
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 760),
child: child,
),
),
);
}
}
class PrototypeCard extends StatelessWidget {
const PrototypeCard({
super.key,
required this.child,
this.padding = const EdgeInsets.all(16),
this.margin,
this.radius = 14,
this.decoration,
});
final Widget child;
final EdgeInsetsGeometry padding;
final EdgeInsetsGeometry? margin;
final double radius;
final BoxDecoration? decoration;
@override
Widget build(BuildContext context) {
final cs = ShadTheme.of(context).colorScheme;
return Container(
margin: margin,
decoration: decoration ??
BoxDecoration(
color: cs.card,
border: Border.all(color: cs.border, width: 0.5),
borderRadius: BorderRadius.circular(radius),
),
child: Padding(padding: padding, child: child),
);
}
}
class PrototypePill extends StatelessWidget {
const PrototypePill({
super.key,
required this.label,
this.selected = false,
this.leading,
this.onTap,
});
final String label;
final bool selected;
final Widget? leading;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
final cs = ShadTheme.of(context).colorScheme;
final bg = selected ? cs.secondary : cs.background;
final fg = selected ? cs.secondaryForeground : cs.mutedForeground;
final borderColor = selected ? const Color(0xFFE3D6B4) : cs.border;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(999),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(999),
border: Border.all(color: borderColor, width: 0.5),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (leading != null) ...[leading!, const SizedBox(width: 4)],
Text(
label,
style: TextStyle(
fontSize: 12,
fontWeight: selected ? FontWeight.w600 : FontWeight.w500,
color: fg,
),
),
],
),
),
);
}
}
class PrototypeMetricChip extends StatelessWidget {
const PrototypeMetricChip({
super.key,
required this.label,
required this.value,
this.valueColor,
this.background,
});
final String label;
final String value;
final Color? valueColor;
final Color? background;
@override
Widget build(BuildContext context) {
final cs = ShadTheme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: background ?? cs.background,
borderRadius: BorderRadius.circular(999),
border: Border.all(color: cs.border, width: 0.5),
),
child: RichText(
text: TextSpan(
style: TextStyle(fontSize: 10, color: cs.mutedForeground),
children: [
TextSpan(text: '$label '),
TextSpan(
text: value,
style: TextStyle(
fontWeight: FontWeight.w600,
color: valueColor ?? cs.foreground,
),
),
],
),
),
);
}
}
class PrototypeLineChart extends StatelessWidget {
const PrototypeLineChart({
super.key,
required this.values,
this.lineColor = const Color(0xFFB5862A),
this.fillColor = const Color(0xFFF7EFD9),
});
final List<double> values;
final Color lineColor;
final Color fillColor;
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _PrototypeLineChartPainter(
values: values,
lineColor: lineColor,
fillColor: fillColor,
),
size: Size.infinite,
);
}
}
class _PrototypeLineChartPainter extends CustomPainter {
_PrototypeLineChartPainter({
required this.values,
required this.lineColor,
required this.fillColor,
});
final List<double> values;
final Color lineColor;
final Color fillColor;
@override
void paint(Canvas canvas, Size size) {
if (values.isEmpty || size.isEmpty) return;
final paintGrid = Paint()
..color = const Color(0xFFECEAE3)
..strokeWidth = 1;
final paintLine = Paint()
..color = lineColor
..style = PaintingStyle.stroke
..strokeWidth = 2.4
..strokeJoin = StrokeJoin.round
..strokeCap = StrokeCap.round;
final paintFill = Paint()
..color = fillColor
..style = PaintingStyle.fill;
const padTop = 10.0;
const padBottom = 10.0;
final plotHeight = size.height - padTop - padBottom;
final plotWidth = size.width;
final min = values.reduce((a, b) => a < b ? a : b);
final max = values.reduce((a, b) => a > b ? a : b);
final span = (max - min).abs() < 0.0001 ? 1.0 : max - min;
Offset pointFor(int index) {
final x = values.length == 1
? plotWidth / 2
: index / (values.length - 1) * plotWidth;
final y = padTop + (1 - (values[index] - min) / span) * plotHeight;
return Offset(x, y);
}
for (var i = 0; i < 4; i++) {
final dy = padTop + plotHeight * i / 3;
canvas.drawLine(Offset(0, dy), Offset(size.width, dy), paintGrid);
}
final pts = List.generate(values.length, pointFor);
final path = Path()..moveTo(pts.first.dx, pts.first.dy);
for (final p in pts.skip(1)) {
path.lineTo(p.dx, p.dy);
}
final fillPath = Path()
..addPath(path, Offset.zero)
..lineTo(size.width, size.height)
..lineTo(0, size.height)
..close();
canvas.drawPath(fillPath, paintFill);
canvas.drawPath(path, paintLine);
final last = pts.last;
canvas.drawCircle(last, 3.8, Paint()..color = lineColor);
}
@override
bool shouldRepaint(covariant _PrototypeLineChartPainter oldDelegate) {
return oldDelegate.values != values ||
oldDelegate.lineColor != lineColor ||
oldDelegate.fillColor != fillColor;
}
}
class PrototypeSectionLabel extends StatelessWidget {
const PrototypeSectionLabel(this.label, {super.key, this.trailing});
final String label;
final Widget? trailing;
@override
Widget build(BuildContext context) {
final cs = ShadTheme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Row(
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: cs.foreground,
),
),
const Spacer(),
if (trailing != null) trailing!,
],
),
);
}
}
class PrototypeDivider extends StatelessWidget {
const PrototypeDivider({super.key, this.margin = const EdgeInsets.symmetric(vertical: 10)});
final EdgeInsetsGeometry margin;
@override
Widget build(BuildContext context) {
final cs = ShadTheme.of(context).colorScheme;
return Container(
margin: margin,
height: 0.5,
color: cs.border,
);
}
}