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; }