feat: add vehicle customization, Arabic search normalization, and smooth movement interpolation

This commit is contained in:
Hamza-Ayed
2026-09-19 12:34:25 +03:00
parent 43ad2e0ad4
commit 55830beee8
16 changed files with 2295 additions and 393 deletions
@@ -0,0 +1,383 @@
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
class VehicleStyleOption {
final String id;
final String labelAr;
final IconData icon;
const VehicleStyleOption({
required this.id,
required this.labelAr,
required this.icon,
});
}
class VehicleColorOption {
final int colorValue;
final String nameAr;
const VehicleColorOption({
required this.colorValue,
required this.nameAr,
});
Color get color => Color(colorValue);
}
class VehicleIconGenerator {
VehicleIconGenerator._();
static const List<VehicleStyleOption> availableStyles = [
VehicleStyleOption(
id: 'car',
labelAr: 'سيارة عصرية 3D',
icon: Icons.directions_car_filled_rounded,
),
VehicleStyleOption(
id: 'suv',
labelAr: 'دفع رباعي تكتيكي',
icon: Icons.airport_shuttle_rounded,
),
VehicleStyleOption(
id: 'arrow',
labelAr: 'سهم الملاحة المجسم',
icon: Icons.navigation_rounded,
),
];
static const List<VehicleColorOption> availableColors = [
VehicleColorOption(colorValue: 0xFF007AFF, nameAr: 'أزرق سيرو'),
VehicleColorOption(colorValue: 0xFF00C853, nameAr: 'أخضر تكتيكي'),
VehicleColorOption(colorValue: 0xFFFF3B30, nameAr: 'أحمر رياضي'),
VehicleColorOption(colorValue: 0xFFFF9500, nameAr: 'برتقالي شمسي'),
VehicleColorOption(colorValue: 0xFF212529, nameAr: 'أسود كربوني'),
VehicleColorOption(colorValue: 0xFF00E5FF, nameAr: 'سماوي كهربائي'),
];
/// Generates crisp PNG image bytes for a vehicle icon based on style and color.
/// Standard canvas size is 192x192 px to ensure crystal clear presentation on high-DPI displays.
static Future<Uint8List> generateVehicleIconBytes({
required String styleId,
required Color primaryColor,
double size = 192.0,
}) async {
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder, Rect.fromLTWH(0, 0, size, size));
switch (styleId) {
case 'suv':
_drawSuv(canvas, size, primaryColor);
break;
case 'arrow':
_drawNavigationArrow(canvas, size, primaryColor);
break;
case 'car':
default:
_drawSedanCar(canvas, size, primaryColor);
break;
}
final picture = recorder.endRecording();
final img = await picture.toImage(size.toInt(), size.toInt());
final byteData = await img.toByteData(format: ui.ImageByteFormat.png);
return byteData!.buffer.asUint8List();
}
// ── 1. MODERN 3D SEDAN CAR ────────────────────────────────────
static void _drawSedanCar(Canvas canvas, double size, Color color) {
final center = Offset(size / 2, size / 2);
final width = size * 0.46;
final height = size * 0.82;
// 1. Soft Realistic Drop Shadow underneath
final shadowPaint = Paint()
..color = const Color(0x55000000)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 12.0);
final shadowRect = RRect.fromRectAndRadius(
Rect.fromCenter(center: center.translate(0, 6), width: width * 1.05, height: height * 1.02),
const Radius.circular(24),
);
canvas.drawRRect(shadowRect, shadowPaint);
// 2. Main Car Chassis Body
final chassisRect = RRect.fromRectAndRadius(
Rect.fromCenter(center: center, width: width, height: height),
const Radius.circular(22),
);
// Glossy metallic gradient across body
final hsl = HSLColor.fromColor(color);
final lightColor = hsl.withLightness((hsl.lightness + 0.18).clamp(0.0, 1.0)).toColor();
final darkColor = hsl.withLightness((hsl.lightness - 0.15).clamp(0.0, 1.0)).toColor();
final bodyPaint = Paint()
..shader = ui.Gradient.linear(
Offset(center.dx - width / 2, center.dy),
Offset(center.dx + width / 2, center.dy),
[darkColor, lightColor, color, darkColor],
[0.0, 0.35, 0.7, 1.0],
);
canvas.drawRRect(chassisRect, bodyPaint);
// Subtle 3D white chassis outline
final outlinePaint = Paint()
..color = Colors.white.withValues(alpha: 0.45)
..style = PaintingStyle.stroke
..strokeWidth = 2.2;
canvas.drawRRect(chassisRect, outlinePaint);
// 3. Front Windshield (curved dark glass)
final windshieldPaint = Paint()
..shader = ui.Gradient.linear(
Offset(center.dx, center.dy - height * 0.28),
Offset(center.dx, center.dy - height * 0.08),
[const Color(0xDD1A2332), const Color(0x992C3E50)],
);
final windshieldPath = Path()
..moveTo(center.dx - width * 0.36, center.dy - height * 0.12)
..lineTo(center.dx - width * 0.28, center.dy - height * 0.26)
..quadraticBezierTo(
center.dx, center.dy - height * 0.30,
center.dx + width * 0.28, center.dy - height * 0.26,
)
..lineTo(center.dx + width * 0.36, center.dy - height * 0.12)
..close();
canvas.drawPath(windshieldPath, windshieldPaint);
// 4. Car Roof / Sunroof
final roofRect = RRect.fromRectAndRadius(
Rect.fromCenter(
center: Offset(center.dx, center.dy + height * 0.02),
width: width * 0.65,
height: height * 0.28,
),
const Radius.circular(8),
);
final roofPaint = Paint()
..color = hsl.withLightness((hsl.lightness - 0.08).clamp(0.0, 1.0)).toColor();
canvas.drawRRect(roofRect, roofPaint);
// Panoramic Sunroof glass
final sunroofRect = RRect.fromRectAndRadius(
Rect.fromCenter(
center: Offset(center.dx, center.dy + height * 0.02),
width: width * 0.52,
height: height * 0.20,
),
const Radius.circular(6),
);
canvas.drawRRect(
sunroofRect,
Paint()..color = const Color(0xBB1E293B),
);
// 5. Rear Windshield
final rearGlassPath = Path()
..moveTo(center.dx - width * 0.35, center.dy + height * 0.18)
..lineTo(center.dx - width * 0.27, center.dy + height * 0.30)
..quadraticBezierTo(
center.dx, center.dy + height * 0.32,
center.dx + width * 0.27, center.dy + height * 0.30,
)
..lineTo(center.dx + width * 0.35, center.dy + height * 0.18)
..close();
canvas.drawPath(
rearGlassPath,
Paint()..color = const Color(0xDD1A2332),
);
// 6. LED Headlights (Glowing Cyan-White at front)
final headlightPaint = Paint()..color = const Color(0xFFF8FAFC);
final headlightGlow = Paint()
..color = const Color(0x8838BDF8)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4.0);
// Left & right headlights
final leftLight = Rect.fromLTWH(center.dx - width * 0.42, center.dy - height * 0.46, width * 0.22, height * 0.08);
final rightLight = Rect.fromLTWH(center.dx + width * 0.20, center.dy - height * 0.46, width * 0.22, height * 0.08);
canvas.drawRRect(RRect.fromRectAndRadius(leftLight, const Radius.circular(4)), headlightGlow);
canvas.drawRRect(RRect.fromRectAndRadius(leftLight, const Radius.circular(4)), headlightPaint);
canvas.drawRRect(RRect.fromRectAndRadius(rightLight, const Radius.circular(4)), headlightGlow);
canvas.drawRRect(RRect.fromRectAndRadius(rightLight, const Radius.circular(4)), headlightPaint);
// 7. LED Taillights (Red accent bars at rear)
final taillightPaint = Paint()..color = const Color(0xFFFF2D55);
final leftTail = Rect.fromLTWH(center.dx - width * 0.42, center.dy + height * 0.42, width * 0.22, height * 0.06);
final rightTail = Rect.fromLTWH(center.dx + width * 0.20, center.dy + height * 0.42, width * 0.22, height * 0.06);
canvas.drawRRect(RRect.fromRectAndRadius(leftTail, const Radius.circular(3)), taillightPaint);
canvas.drawRRect(RRect.fromRectAndRadius(rightTail, const Radius.circular(3)), taillightPaint);
// 8. Side Mirrors
final mirrorPaint = Paint()..color = color;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(center.dx - width * 0.58, center.dy - height * 0.18, width * 0.12, height * 0.08),
const Radius.circular(4),
),
mirrorPaint,
);
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(center.dx + width * 0.46, center.dy - height * 0.18, width * 0.12, height * 0.08),
const Radius.circular(4),
),
mirrorPaint,
);
}
// ── 2. TACTICAL 4x4 SUV ───────────────────────────────────────
static void _drawSuv(Canvas canvas, double size, Color color) {
final center = Offset(size / 2, size / 2);
final width = size * 0.52;
final height = size * 0.84;
// Drop Shadow
final shadowPaint = Paint()
..color = const Color(0x66000000)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 14.0);
final shadowRect = RRect.fromRectAndRadius(
Rect.fromCenter(center: center.translate(0, 6), width: width * 1.06, height: height * 1.02),
const Radius.circular(20),
);
canvas.drawRRect(shadowRect, shadowPaint);
// SUV Boxy Rugged Body
final chassisRect = RRect.fromRectAndRadius(
Rect.fromCenter(center: center, width: width, height: height),
const Radius.circular(18),
);
final hsl = HSLColor.fromColor(color);
final darkColor = hsl.withLightness((hsl.lightness - 0.18).clamp(0.0, 1.0)).toColor();
final lightColor = hsl.withLightness((hsl.lightness + 0.15).clamp(0.0, 1.0)).toColor();
final bodyPaint = Paint()
..shader = ui.Gradient.linear(
Offset(center.dx - width / 2, center.dy),
Offset(center.dx + width / 2, center.dy),
[darkColor, lightColor, color, darkColor],
[0.0, 0.35, 0.7, 1.0],
);
canvas.drawRRect(chassisRect, bodyPaint);
// Heavy outline
canvas.drawRRect(
chassisRect,
Paint()
..color = Colors.white.withValues(alpha: 0.5)
..style = PaintingStyle.stroke
..strokeWidth = 2.4,
);
// Roof Rack Crossbars
final rackPaint = Paint()
..color = const Color(0xFF1E293B)
..strokeWidth = 3.5
..style = PaintingStyle.stroke;
canvas.drawLine(
Offset(center.dx - width * 0.36, center.dy - height * 0.12),
Offset(center.dx + width * 0.36, center.dy - height * 0.12),
rackPaint,
);
canvas.drawLine(
Offset(center.dx - width * 0.36, center.dy + height * 0.12),
Offset(center.dx + width * 0.36, center.dy + height * 0.12),
rackPaint,
);
// Front Windshield
final windshieldRect = RRect.fromRectAndRadius(
Rect.fromCenter(
center: Offset(center.dx, center.dy - height * 0.22),
width: width * 0.74,
height: height * 0.18,
),
const Radius.circular(8),
);
canvas.drawRRect(
windshieldRect,
Paint()..color = const Color(0xDD0F172A),
);
// Aggressive LED Front Light Bar
final lightBar = RRect.fromRectAndRadius(
Rect.fromCenter(
center: Offset(center.dx, center.dy - height * 0.44),
width: width * 0.85,
height: height * 0.07,
),
const Radius.circular(4),
);
canvas.drawRRect(
lightBar,
Paint()
..color = const Color(0xAA38BDF8)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5),
);
canvas.drawRRect(lightBar, Paint()..color = Colors.white);
// Rear Brake Light Bar
final rearLightBar = RRect.fromRectAndRadius(
Rect.fromCenter(
center: Offset(center.dx, center.dy + height * 0.44),
width: width * 0.80,
height: height * 0.06,
),
const Radius.circular(3),
);
canvas.drawRRect(rearLightBar, Paint()..color = const Color(0xFFFF2D55));
}
// ── 3. 3D DIRECTIONAL NAVIGATION ARROW (Google / Apple Style) ──
static void _drawNavigationArrow(Canvas canvas, double size, Color color) {
final center = Offset(size / 2, size / 2);
final radius = size * 0.44;
// Glowing outer circular halo
final haloPaint = Paint()
..color = color.withValues(alpha: 0.22)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 14.0);
canvas.drawCircle(center, radius, haloPaint);
// White disc background with elevation shadow
final shadowPaint = Paint()
..color = const Color(0x44000000)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 8.0);
canvas.drawCircle(center.translate(0, 4), radius * 0.78, shadowPaint);
final discPaint = Paint()..color = Colors.white;
canvas.drawCircle(center, radius * 0.78, discPaint);
// Inner directional 3D chevron arrow
final arrowPath = Path()
..moveTo(center.dx, center.dy - radius * 0.65) // Top sharp point
..lineTo(center.dx + radius * 0.52, center.dy + radius * 0.42) // Bottom right wing
..lineTo(center.dx, center.dy + radius * 0.18) // Inner center notch
..lineTo(center.dx - radius * 0.52, center.dy + radius * 0.42) // Bottom left wing
..close();
// 3D Split Lighting on Chevron
final hsl = HSLColor.fromColor(color);
final lightHalf = hsl.withLightness((hsl.lightness + 0.12).clamp(0.0, 1.0)).toColor();
final darkHalf = hsl.withLightness((hsl.lightness - 0.14).clamp(0.0, 1.0)).toColor();
final arrowPaint = Paint()
..shader = ui.Gradient.linear(
Offset(center.dx - radius * 0.5, center.dy),
Offset(center.dx + radius * 0.5, center.dy),
[darkHalf, lightHalf],
);
canvas.drawPath(arrowPath, arrowPaint);
// Center divider line
final linePaint = Paint()
..color = Colors.white.withValues(alpha: 0.55)
..strokeWidth = 2.0;
canvas.drawLine(
Offset(center.dx, center.dy - radius * 0.62),
Offset(center.dx, center.dy + radius * 0.18),
linePaint,
);
}
}