import 'dart:math' as math; import 'package:flutter/material.dart'; import '../../domain/game/game.dart'; import '../theme/theme.dart'; class WheelPainter extends CustomPainter { final List gamePrizes; final Color Function(GamePrize prize, int index) getPrizeColor; WheelPainter({required this.gamePrizes, required this.getPrizeColor}); @override void paint(Canvas canvas, Size size) { if (gamePrizes.isEmpty) return; final center = Offset(size.width / 2, size.height / 2); final radius = size.width / 2; final sectionAngle = 2 * math.pi / gamePrizes.length; // Draw outer white border final outerBorderPaint = Paint() ..color = Colors.white ..style = PaintingStyle.fill; canvas.drawCircle(center, radius, outerBorderPaint); // Draw blue border ring final blueBorderPaint = Paint() ..color = const Color(0xFF3B82F6) ..style = PaintingStyle.fill; canvas.drawCircle(center, radius - 8, blueBorderPaint); // Draw inner white circle final innerWhitePaint = Paint() ..color = Colors.white ..style = PaintingStyle.fill; canvas.drawCircle(center, radius - 20, innerWhitePaint); // Draw sections for (int i = 0; i < gamePrizes.length; i++) { final prize = gamePrizes[i]; final startAngle = i * sectionAngle - math.pi / 2; // Section background final sectionPaint = Paint() ..color = getPrizeColor(prize, i) ..style = PaintingStyle.fill; canvas.drawArc( Rect.fromCircle(center: center, radius: radius - 22), startAngle, sectionAngle, true, sectionPaint, ); // Draw icon in each section final iconAngle = startAngle + sectionAngle / 2; final iconPosition = Offset( center.dx + (radius - 60) * math.cos(iconAngle), center.dy + (radius - 60) * math.sin(iconAngle), ); // Draw icon background circle final iconBgPaint = Paint() ..color = Colors.white ..style = PaintingStyle.fill; canvas.drawCircle(iconPosition, 16, iconBgPaint); // Save canvas state for icon drawing canvas.save(); canvas.translate(iconPosition.dx, iconPosition.dy); // Get icon from metadata or use default final iconData = _getIconFromMetadata(prize.metadata); final iconText = _getIconText(iconData); final iconTextPainter = TextPainter( text: TextSpan( text: iconText, style: TextStyle( fontFamily: 'MaterialIcons', fontSize: 20, color: getPrizeColor(prize, i), fontWeight: FontWeight.normal, ), ), textDirection: TextDirection.ltr, ); iconTextPainter.layout(); iconTextPainter.paint( canvas, Offset(-iconTextPainter.width / 2, -iconTextPainter.height / 2), ); canvas.restore(); // Draw prize text final textAngle = startAngle + sectionAngle / 2; final textPosition = Offset( center.dx + (radius - 100) * math.cos(textAngle), center.dy + (radius - 100) * math.sin(textAngle), ); // Save canvas state for text rotation canvas.save(); canvas.translate(textPosition.dx, textPosition.dy); canvas.rotate(textAngle + math.pi / 2); final textPainter = TextPainter( text: TextSpan( text: prize.name, style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), textDirection: TextDirection.ltr, ); textPainter.layout(); textPainter.paint( canvas, Offset(-textPainter.width / 2, -textPainter.height / 2), ); canvas.restore(); // Draw stock indicator if stock is low if (prize.stock <= 5 && prize.stock > 0) { final stockAngle = startAngle + sectionAngle / 2; final stockPosition = Offset( center.dx + (radius - 35) * math.cos(stockAngle), center.dy + (radius - 35) * math.sin(stockAngle), ); final stockPaint = Paint() ..color = AppColor.error ..style = PaintingStyle.fill; canvas.drawCircle(stockPosition, 8, stockPaint); canvas.save(); canvas.translate(stockPosition.dx, stockPosition.dy); final stockTextPainter = TextPainter( text: TextSpan( text: '!', style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), textDirection: TextDirection.ltr, ); stockTextPainter.layout(); stockTextPainter.paint( canvas, Offset(-stockTextPainter.width / 2, -stockTextPainter.height / 2), ); canvas.restore(); } } // Draw white dots around the outer edge final dotPaint = Paint() ..color = Colors.white ..style = PaintingStyle.fill; for (int i = 0; i < 24; i++) { final dotAngle = (2 * math.pi / 24) * i; final dotPosition = Offset( center.dx + (radius - 14) * math.cos(dotAngle), center.dy + (radius - 14) * math.sin(dotAngle), ); canvas.drawCircle(dotPosition, 3, dotPaint); } // Draw section dividers final dividerPaint = Paint() ..color = AppColor.white ..style = PaintingStyle.stroke ..strokeWidth = 2; for (int i = 0; i < gamePrizes.length; i++) { final angle = i * sectionAngle - math.pi / 2; final lineStart = Offset( center.dx + (radius - 130) * math.cos(angle), center.dy + (radius - 130) * math.sin(angle), ); final lineEnd = Offset( center.dx + (radius - 22) * math.cos(angle), center.dy + (radius - 22) * math.sin(angle), ); canvas.drawLine(lineStart, lineEnd, dividerPaint); } } IconData _getIconFromMetadata(Map metadata) { final iconName = metadata['icon'] as String?; switch (iconName?.toLowerCase()) { case 'card_giftcard': return Icons.card_giftcard; case 'monetization_on': return Icons.monetization_on; case 'redeem': return Icons.redeem; case 'attach_money': return Icons.attach_money; case 'account_balance_wallet': return Icons.account_balance_wallet; case 'diamond': return Icons.diamond; case 'star': return Icons.star; case 'emoji_events': return Icons.emoji_events; case 'refresh': return Icons.refresh; case 'visibility': return Icons.visibility; default: return Icons.card_giftcard; // Default icon } } String _getIconText(IconData icon) { // Convert IconData to Unicode string for drawing switch (icon.codePoint) { case 0xe8f4: // Icons.visibility return String.fromCharCode(0xe8f4); case 0xe8f5: // Icons.visibility_off return String.fromCharCode(0xe8f5); case 0xe850: // Icons.account_balance_wallet return String.fromCharCode(0xe850); case 0xe151: // Icons.card_giftcard return String.fromCharCode(0xe151); case 0xe5d5: // Icons.refresh return String.fromCharCode(0xe5d5); case 0xe263: // Icons.attach_money return String.fromCharCode(0xe263); case 0xe8a1: // Icons.redeem return String.fromCharCode(0xe8a1); case 0xe57d: // Icons.monetization_on return String.fromCharCode(0xe57d); case 0xe5ca: // Icons.diamond return String.fromCharCode(0xe5ca); case 0xe838: // Icons.star return String.fromCharCode(0xe838); case 0xe2a3: // Icons.emoji_events return String.fromCharCode(0xe2a3); default: return String.fromCharCode(0xe151); // Default card_giftcard icon } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }