281 lines
8.2 KiB
Dart
281 lines
8.2 KiB
Dart
import 'dart:math' as math;
|
|
import 'package:flutter/material.dart';
|
|
|
|
/// A custom background wrapper that renders a highly premium, subtle
|
|
/// vector pattern of noodle bowls, wavy noodles, chopsticks, and spices (star anise/leaves)
|
|
/// watermark in the background of the screen.
|
|
class RestaurantBackground extends StatelessWidget {
|
|
final Widget child;
|
|
|
|
const RestaurantBackground({super.key, required this.child});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
return Stack(
|
|
children: [
|
|
Positioned.fill(
|
|
child: CustomPaint(
|
|
painter: NoodlePatternPainter(
|
|
color: colorScheme.onSurface.withValues(alpha: 0.08),
|
|
),
|
|
),
|
|
),
|
|
child,
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class NoodlePatternPainter extends CustomPainter {
|
|
final Color color;
|
|
|
|
NoodlePatternPainter({required this.color});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
// Use a fixed-seed Random generator so coordinates remain perfectly stable
|
|
// on every repaint (preventing elements from flickering/moving).
|
|
final random = math.Random(5758);
|
|
|
|
// Jittered grid parameters adjusted for larger scales
|
|
const double stepX = 145.0;
|
|
const double stepY = 145.0;
|
|
|
|
int index = 0;
|
|
for (double y = 40; y < size.height; y += stepY) {
|
|
final double startX = (index % 2 == 0) ? 35.0 : 100.0;
|
|
for (double x = startX; x < size.width; x += stepX) {
|
|
// Apply random jitter of up to +/- 30 pixels to x and y coordinates
|
|
final double jitterX = (random.nextDouble() - 0.5) * 60.0;
|
|
final double jitterY = (random.nextDouble() - 0.5) * 60.0;
|
|
final double drawX = x + jitterX;
|
|
final double drawY = y + jitterY;
|
|
|
|
// Skip drawing if the jittered offset pushes it outside the screen
|
|
if (drawX < 0 ||
|
|
drawX > size.width ||
|
|
drawY < 0 ||
|
|
drawY > size.height) {
|
|
continue;
|
|
}
|
|
|
|
// Random rotation between -0.6 and +0.6 radians
|
|
final double rotation = (random.nextDouble() - 0.5) * 1.2;
|
|
|
|
// Random scale factor enlarged to range between 1.1x and 1.5x
|
|
final double scale = 1.1 + random.nextDouble() * 0.4;
|
|
|
|
// Randomize the element's opacity slightly relative to the base color
|
|
final double alphaScale = 0.6 + random.nextDouble() * 0.8;
|
|
final elementColor = color.withValues(
|
|
alpha: (color.alpha / 255.0 * alphaScale).clamp(0.02, 0.15),
|
|
);
|
|
|
|
final paint = Paint()
|
|
..color = elementColor
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 1.0
|
|
..strokeCap = StrokeCap.round;
|
|
|
|
final fillPaint = Paint()
|
|
..color = elementColor
|
|
..style = PaintingStyle.fill;
|
|
|
|
// Randomly select one of the 3 restaurant/noodle categories to draw
|
|
final int elementIndex = random.nextInt(3);
|
|
|
|
canvas.save();
|
|
canvas.translate(drawX, drawY);
|
|
canvas.rotate(rotation);
|
|
canvas.scale(scale);
|
|
|
|
if (elementIndex == 0) {
|
|
_drawBowl(canvas, paint);
|
|
} else if (elementIndex == 1) {
|
|
_drawDrumstick(canvas, paint, fillPaint);
|
|
} else {
|
|
final vegIndex = random.nextInt(5);
|
|
if (vegIndex == 0) {
|
|
_drawMushroom(canvas, paint, fillPaint);
|
|
} else if (vegIndex == 1) {
|
|
_drawCarrot(canvas, paint, fillPaint);
|
|
} else if (vegIndex == 2) {
|
|
_drawBroccoli(canvas, paint, fillPaint);
|
|
} else if (vegIndex == 3) {
|
|
_drawChili(canvas, paint, fillPaint);
|
|
} else {
|
|
_drawTomato(canvas, paint, fillPaint);
|
|
}
|
|
}
|
|
|
|
canvas.restore();
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
|
|
void _drawBowl(Canvas canvas, Paint paint) {
|
|
// Draw a noodle bowl outline
|
|
final path = Path()
|
|
..moveTo(-12, -4)
|
|
..arcTo(Rect.fromLTWH(-12, -10, 24, 18), 0, 3.14159, false)
|
|
..lineTo(-12, -4)
|
|
..close();
|
|
canvas.drawPath(path, paint);
|
|
|
|
// Rim lines
|
|
canvas.drawLine(const Offset(-13, -4), const Offset(13, -4), paint);
|
|
|
|
// Steam/Noodles rising
|
|
final steam = Path()
|
|
..moveTo(-5, -6)
|
|
..quadraticBezierTo(-7, -10, -5, -15)
|
|
..moveTo(0, -6)
|
|
..quadraticBezierTo(-2, -11, 0, -16)
|
|
..moveTo(5, -6)
|
|
..quadraticBezierTo(3, -10, 5, -15);
|
|
canvas.drawPath(steam, paint);
|
|
}
|
|
|
|
void _drawDrumstick(Canvas canvas, Paint paint, Paint fillPaint) {
|
|
// Plump cartoon-style chicken drumstick with asymmetric meaty bulge
|
|
final path = Path()
|
|
// Bone shaft left
|
|
..moveTo(-1.5, 3)
|
|
..lineTo(-1.5, 9)
|
|
// Left bone joint knob
|
|
..cubicTo(-3.5, 9, -4.5, 11.5, -2.5, 13.5)
|
|
..cubicTo(-1.0, 14.5, 0.0, 12.0, 0.0, 11.0)
|
|
// Right bone joint knob
|
|
..cubicTo(0.0, 12.0, 1.0, 14.5, 2.5, 13.5)
|
|
..cubicTo(4.5, 11.5, 3.5, 9, 1.5, 9)
|
|
// Bone shaft right
|
|
..lineTo(1.5, 3)
|
|
// Taper out to meaty part
|
|
..lineTo(5.5, 0)
|
|
// Asymmetric bulbous meat bulge (wide to the right, flatter on the left)
|
|
..cubicTo(12.0, -3.5, 8.5, -12, 0, -12)
|
|
..cubicTo(-6.0, -12, -7.0, -3.5, -5.5, 0)
|
|
..close();
|
|
|
|
canvas.drawPath(path, paint);
|
|
canvas.drawPath(path, fillPaint);
|
|
}
|
|
|
|
void _drawChili(Canvas canvas, Paint paint, Paint fillPaint) {
|
|
canvas.save();
|
|
canvas.rotate(0.3); // Slight tilt for organic look
|
|
final body = Path()
|
|
..moveTo(-3, -7)
|
|
..quadraticBezierTo(2, -7, 4, -3)
|
|
..quadraticBezierTo(5, 3, -4, 9) // Pointy pepper tip
|
|
..quadraticBezierTo(-3, 2, -3, -7)
|
|
..close();
|
|
canvas.drawPath(body, paint);
|
|
canvas.drawPath(body, fillPaint);
|
|
|
|
// Stem at the top
|
|
final stem = Path()
|
|
..moveTo(0, -7)
|
|
..quadraticBezierTo(1, -12, -3, -13);
|
|
canvas.drawPath(stem, paint);
|
|
canvas.restore();
|
|
}
|
|
|
|
void _drawTomato(Canvas canvas, Paint paint, Paint fillPaint) {
|
|
// Round tomato body
|
|
final body = Path()
|
|
..addOval(
|
|
Rect.fromCenter(center: const Offset(0, 1), width: 14, height: 12),
|
|
);
|
|
canvas.drawPath(body, paint);
|
|
canvas.drawPath(body, fillPaint);
|
|
|
|
// Star-shaped leafy top
|
|
final leaves = Path()
|
|
..moveTo(0, -5)
|
|
..lineTo(-2, -8)
|
|
..moveTo(0, -5)
|
|
..lineTo(2, -8)
|
|
..moveTo(0, -5)
|
|
..lineTo(0, -9)
|
|
..moveTo(0, -5)
|
|
..quadraticBezierTo(-1, -7, -3.5, -6)
|
|
..moveTo(0, -5)
|
|
..quadraticBezierTo(1, -7, 3.5, -6);
|
|
canvas.drawPath(leaves, paint);
|
|
}
|
|
|
|
void _drawMushroom(Canvas canvas, Paint paint, Paint fillPaint) {
|
|
// Cap
|
|
final cap = Path()
|
|
..moveTo(-9, 0)
|
|
..arcTo(Rect.fromLTWH(-9, -8, 18, 11), 3.14159, 3.14159, false)
|
|
..close();
|
|
canvas.drawPath(cap, paint);
|
|
canvas.drawPath(cap, fillPaint);
|
|
|
|
// Stalk
|
|
final stalk = Path()
|
|
..moveTo(-3, 0)
|
|
..lineTo(-3, 6)
|
|
..quadraticBezierTo(0, 8, 3, 6)
|
|
..lineTo(3, 0)
|
|
..close();
|
|
canvas.drawPath(stalk, paint);
|
|
canvas.drawPath(stalk, fillPaint);
|
|
}
|
|
|
|
void _drawCarrot(Canvas canvas, Paint paint, Paint fillPaint) {
|
|
canvas.save();
|
|
canvas.rotate(-0.785); // Point carrot downwards
|
|
|
|
// Carrot cone root body
|
|
final root = Path()
|
|
..moveTo(-4, -6)
|
|
..lineTo(4, -6)
|
|
..lineTo(0, 9) // tip
|
|
..close();
|
|
canvas.drawPath(root, paint);
|
|
canvas.drawPath(root, fillPaint);
|
|
|
|
// Leaves at the top
|
|
final leaves = Path()
|
|
..moveTo(-2, -6)
|
|
..quadraticBezierTo(-4, -12, -6, -10)
|
|
..moveTo(0, -6)
|
|
..quadraticBezierTo(0, -14, 2, -12)
|
|
..moveTo(2, -6)
|
|
..quadraticBezierTo(4, -11, 6, -9);
|
|
canvas.drawPath(leaves, paint);
|
|
|
|
canvas.restore();
|
|
}
|
|
|
|
void _drawBroccoli(Canvas canvas, Paint paint, Paint fillPaint) {
|
|
// Stem
|
|
final stem = Path()
|
|
..moveTo(-2.5, 2)
|
|
..lineTo(-2.5, 8)
|
|
..quadraticBezierTo(0, 10, 2.5, 8)
|
|
..lineTo(2.5, 2)
|
|
..close();
|
|
canvas.drawPath(stem, paint);
|
|
canvas.drawPath(stem, fillPaint);
|
|
|
|
// Crown cloud shape
|
|
final crown = Path()
|
|
..moveTo(-7, 2)
|
|
..cubicTo(-11, 2, -11, -5, -5, -5)
|
|
..cubicTo(-5, -11, 5, -11, 5, -5)
|
|
..cubicTo(11, -5, 11, 2, 7, 2)
|
|
..close();
|
|
canvas.drawPath(crown, paint);
|
|
canvas.drawPath(crown, fillPaint);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
|
}
|