enaklo-flutter/lib/common/painter/wheel_painter.dart
2025-09-18 14:53:39 +07:00

149 lines
4.5 KiB
Dart

// wheel_painter.dart - Fixed implementation with consistent positioning
import 'dart:math' as math;
import 'package:flutter/material.dart';
import '../../domain/game/game.dart';
class WheelPainter extends CustomPainter {
final List<GamePrize> 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 - KONSISTEN dengan logic spin
for (int i = 0; i < gamePrizes.length; i++) {
final prize = gamePrizes[i];
// Section 0 di top (-π/2), section 1 di kanan atas, dst (clockwise)
final startAngle = (-math.pi / 2) + (i * sectionAngle);
// 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 prize text
final textAngle = startAngle + sectionAngle / 2;
final textPosition = Offset(
center.dx + (radius - 80) * math.cos(textAngle),
center.dy + (radius - 80) * math.sin(textAngle),
);
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: 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,
);
numberPainter.layout();
numberPainter.paint(
canvas,
Offset(
numberPosition.dx - numberPainter.width / 2,
numberPosition.dy - numberPainter.height / 2,
),
);
}
// 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(
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 = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 2;
for (int i = 0; i < gamePrizes.length; i++) {
final angle = (-math.pi / 2) + (i * sectionAngle);
final lineStart = Offset(
center.dx + (radius - 110) * math.cos(angle),
center.dy + (radius - 110) * 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);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}