Files
gopeed/ui/flutter/lib/shared/widgets/app_empty_illustration.dart

218 lines
7.3 KiB
Dart

import 'package:flutter/widgets.dart';
import '../theme/app_palette.dart';
enum AppEmptyIllustrationType { inbox, puzzle, search, connectionError, documentError }
/// Context-specific drawings share the same muted colors and rounded strokes.
class AppEmptyIllustration extends StatelessWidget {
const AppEmptyIllustration({super.key, required this.type});
final AppEmptyIllustrationType type;
@override
Widget build(BuildContext context) {
final palette = AppPalette.of(context);
return CustomPaint(
size: const Size(148, 108),
painter: type == AppEmptyIllustrationType.inbox
? _InboxIllustrationPainter(palette: palette)
: _ContextIllustrationPainter(palette: palette, type: type),
);
}
}
class _ContextIllustrationPainter extends CustomPainter {
const _ContextIllustrationPainter({required this.palette, required this.type});
final AppPalette palette;
final AppEmptyIllustrationType type;
Paint get _outline => Paint()
..color = palette.textSecondary.withValues(alpha: 0.72)
..style = PaintingStyle.stroke
..strokeWidth = 1.8
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round;
Paint get _detail => Paint()
..color = palette.textMuted.withValues(alpha: 0.34)
..style = PaintingStyle.stroke
..strokeWidth = 2
..strokeCap = StrokeCap.round;
Paint get _fill => Paint()..color = palette.surfaceSoft;
@override
void paint(Canvas canvas, Size size) {
canvas.save();
canvas.scale(size.width / 148, size.height / 108);
switch (type) {
case AppEmptyIllustrationType.puzzle:
_paintPuzzle(canvas);
case AppEmptyIllustrationType.search:
_paintSearch(canvas);
case AppEmptyIllustrationType.connectionError:
_paintConnectionError(canvas);
case AppEmptyIllustrationType.documentError:
_paintDocumentError(canvas);
case AppEmptyIllustrationType.inbox:
break;
}
canvas.restore();
}
void _paintPuzzle(Canvas canvas) {
canvas.translate(9, 0);
final piece = Path()
..moveTo(42, 29)
..lineTo(62, 29)
..cubicTo(55, 10, 86, 10, 79, 29)
..lineTo(98, 29)
..quadraticBezierTo(104, 29, 104, 35)
..lineTo(104, 49)
..cubicTo(84, 42, 84, 73, 104, 66)
..lineTo(104, 86)
..quadraticBezierTo(104, 92, 98, 92)
..lineTo(79, 92)
..cubicTo(86, 73, 55, 73, 62, 92)
..lineTo(42, 92)
..quadraticBezierTo(36, 92, 36, 86)
..lineTo(36, 66)
..cubicTo(17, 73, 17, 42, 36, 49)
..lineTo(36, 35)
..quadraticBezierTo(36, 29, 42, 29)
..close();
canvas.drawPath(piece, _fill);
canvas.drawPath(piece, _outline);
}
void _paintSearch(Canvas canvas) {
final lens = const Offset(64, 45);
canvas.drawCircle(lens, 29, _fill);
canvas.drawCircle(lens, 29, _outline);
canvas.drawLine(const Offset(85, 67), const Offset(115, 96), _outline..strokeWidth = 4);
canvas.drawLine(const Offset(53, 45), const Offset(75, 45), _detail);
canvas.drawLine(const Offset(27, 85), const Offset(41, 85), _detail);
canvas.drawCircle(const Offset(112, 23), 3, Paint()..color = palette.border);
}
void _paintConnectionError(Canvas canvas) {
final cloud = Path()
..moveTo(40, 70)
..cubicTo(21, 70, 19, 43, 39, 41)
..cubicTo(40, 14, 77, 8, 91, 32)
..cubicTo(111, 23, 129, 40, 121, 56)
..cubicTo(133, 66, 119, 79, 108, 74)
..lineTo(40, 74)
..close();
canvas.drawPath(cloud, _fill);
canvas.drawPath(cloud, _outline);
canvas.drawLine(const Offset(74, 74), const Offset(74, 82), _detail);
canvas.drawLine(const Offset(48, 96), const Offset(63, 96), _outline);
canvas.drawLine(const Offset(86, 96), const Offset(101, 96), _outline);
canvas.drawLine(const Offset(71, 91), const Offset(77, 101), _detail);
canvas.drawCircle(const Offset(45, 96), 2, _fill);
canvas.drawCircle(const Offset(104, 96), 2, _fill);
}
void _paintDocumentError(Canvas canvas) {
final document = Path()
..moveTo(47, 12)
..lineTo(89, 12)
..lineTo(107, 30)
..lineTo(107, 89)
..quadraticBezierTo(107, 96, 100, 96)
..lineTo(47, 96)
..quadraticBezierTo(40, 96, 40, 89)
..lineTo(40, 19)
..quadraticBezierTo(40, 12, 47, 12)
..close();
canvas.drawPath(document, _fill);
canvas.drawPath(document, _outline);
canvas.drawPath(
Path()
..moveTo(89, 12)
..lineTo(89, 30)
..lineTo(107, 30),
_detail,
);
for (final y in [43.0, 56.0, 69.0]) {
canvas.drawLine(Offset(53, y), Offset(y == 69 ? 74 : 91, y), _detail);
}
canvas.drawCircle(const Offset(107, 85), 16, Paint()..color = palette.cardBg);
canvas.drawCircle(const Offset(107, 85), 16, _outline);
canvas.drawLine(const Offset(107, 77), const Offset(107, 86), _outline);
canvas.drawCircle(const Offset(107, 92), 1.2, Paint()..color = palette.textSecondary.withValues(alpha: 0.72));
}
@override
bool shouldRepaint(_ContextIllustrationPainter oldDelegate) =>
oldDelegate.palette != palette || oldDelegate.type != type;
}
class _InboxIllustrationPainter extends CustomPainter {
const _InboxIllustrationPainter({required this.palette});
final AppPalette palette;
@override
void paint(Canvas canvas, Size size) {
final borderPaint = Paint()
..color = palette.border
..style = PaintingStyle.stroke
..strokeWidth = 1.4;
final sheetRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 34, 6, 68, 72), const Radius.circular(7));
canvas.drawRRect(sheetRect.shift(const Offset(0, 3)), Paint()..color = palette.textMuted.withValues(alpha: 0.06));
canvas.drawRRect(sheetRect, Paint()..color = palette.cardBg);
canvas.drawRRect(sheetRect, borderPaint);
final rowPaint = Paint()
..color = palette.textMuted.withValues(alpha: 0.34)
..strokeWidth = 2
..strokeCap = StrokeCap.round;
final centerX = size.width / 2;
for (var row = 0; row < 2; row++) {
final y = 20.0 + row * 14;
canvas.drawCircle(Offset(centerX - 22, y), 2.5, Paint()..color = palette.surfaceSoft);
canvas.drawLine(Offset(centerX - 14, y), Offset(centerX + 21, y), rowPaint);
}
final emptyBadgePaint = Paint()
..color = palette.surfaceSoft
..style = PaintingStyle.fill;
canvas.drawCircle(Offset(centerX, 56), 11, emptyBadgePaint);
canvas.drawCircle(Offset(centerX, 56), 11, borderPaint);
canvas.drawLine(Offset(centerX - 4, 56), Offset(centerX + 4, 56), rowPaint);
final trayPath = Path()
..moveTo(26, 63)
..lineTo(49, 63)
..lineTo(57, 75)
..quadraticBezierTo(59, 78, 63, 78)
..lineTo(85, 78)
..quadraticBezierTo(89, 78, 91, 75)
..lineTo(99, 63)
..lineTo(122, 63)
..lineTo(115, 96)
..quadraticBezierTo(114, 101, 108, 101)
..lineTo(40, 101)
..quadraticBezierTo(34, 101, 33, 96)
..close();
canvas.drawPath(trayPath, Paint()..color = palette.surfaceSoft);
final trayPaint = Paint()
..color = palette.textSecondary.withValues(alpha: 0.72)
..style = PaintingStyle.stroke
..strokeWidth = 1.8
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round;
canvas.drawPath(trayPath, trayPaint);
}
@override
bool shouldRepaint(_InboxIllustrationPainter oldDelegate) {
return oldDelegate.palette != palette;
}
}