Ferish Wheel and Music

This commit is contained in:
efrilm
2025-09-18 14:53:39 +07:00
parent 73918430b2
commit 909c312af0
28 changed files with 970 additions and 1422 deletions
+43 -154
View File
@@ -1,8 +1,7 @@
// wheel_painter.dart - Fixed implementation with consistent positioning
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<GamePrize> gamePrizes;
@@ -36,10 +35,11 @@ class WheelPainter extends CustomPainter {
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius - 20, innerWhitePaint);
// Draw sections
// Draw sections - KONSISTEN dengan logic spin
for (int i = 0; i < gamePrizes.length; i++) {
final prize = gamePrizes[i];
final startAngle = i * sectionAngle - math.pi / 2;
// Section 0 di top (-π/2), section 1 di kanan atas, dst (clockwise)
final startAngle = (-math.pi / 2) + (i * sectionAngle);
// Section background
final sectionPaint = Paint()
@@ -54,54 +54,13 @@ class WheelPainter extends CustomPainter {
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),
center.dx + (radius - 80) * math.cos(textAngle),
center.dy + (radius - 80) * math.sin(textAngle),
);
// Save canvas state for text rotation
canvas.save();
canvas.translate(textPosition.dx, textPosition.dy);
canvas.rotate(textAngle + math.pi / 2);
@@ -111,62 +70,51 @@ class WheelPainter extends CustomPainter {
text: prize.name,
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
);
textPainter.layout(maxWidth: 100);
textPainter.paint(
canvas,
Offset(-textPainter.width / 2, -textPainter.height / 2),
);
canvas.restore();
// DEBUG: Draw section number
final numberPosition = Offset(
center.dx + (radius - 50) * math.cos(textAngle),
center.dy + (radius - 50) * math.sin(textAngle),
);
canvas.drawCircle(numberPosition, 15, Paint()..color = Colors.white);
final numberPainter = TextPainter(
text: TextSpan(
text: i.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
numberPainter.layout();
numberPainter.paint(
canvas,
Offset(-textPainter.width / 2, -textPainter.height / 2),
Offset(
numberPosition.dx - numberPainter.width / 2,
numberPosition.dy - numberPainter.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
// Draw white dots
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(
@@ -178,15 +126,14 @@ class WheelPainter extends CustomPainter {
// Draw section dividers
final dividerPaint = Paint()
..color = AppColor.white
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 2;
for (int i = 0; i < gamePrizes.length; i++) {
final angle = i * sectionAngle - math.pi / 2;
final angle = (-math.pi / 2) + (i * sectionAngle);
final lineStart = Offset(
center.dx + (radius - 130) * math.cos(angle),
center.dy + (radius - 130) * math.sin(angle),
center.dx + (radius - 110) * math.cos(angle),
center.dy + (radius - 110) * math.sin(angle),
);
final lineEnd = Offset(
center.dx + (radius - 22) * math.cos(angle),
@@ -196,64 +143,6 @@ class WheelPainter extends CustomPainter {
}
}
IconData _getIconFromMetadata(Map<String, dynamic> 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;
}
+1 -1
View File
@@ -7,7 +7,7 @@ class ApiPath {
static String resend = '/api/v1/customer-auth/resend-otp';
// Marketing
static String gamePrizeByGameId = '/api/v1/marketing/game-prizes/game';
static String ferrisWheel = '/api/v1/customer/ferris-wheel';
// Customer
static String customerPoint = '/api/v1/customer/points';
}