draw updates

This commit is contained in:
Deep Koluguri 2025-11-13 11:07:45 -05:00
parent b1ecd70f8a
commit ac9a0389d1
2 changed files with 231 additions and 519 deletions

View File

@ -569,284 +569,195 @@ class _SlotMachineDrawAnimationState extends State<SlotMachineDrawAnimation>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final winnerName = _winnerId != null
? widget.members.firstWhere((m) => m['id'] == _winnerId)['name']
: null;
return Container( return Container(
width: 300.w, width: 320.w,
height: 520.h, height: 520.h,
child: Column( child: Column(
children: [ children: [
// Title
Text(
'Slot Machine Draw',
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: Colors.orange.shade700,
),
),
SizedBox(height: 20.h),
// Pointer/Indicator
Container(
width: 280.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.keyboard_arrow_down,
color: Colors.red.shade600,
size: 30.w,
),
SizedBox(width: 8.w),
Text(
_isAnimating ? 'Selecting...' : 'Winner!',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.bold,
color: _isAnimating ? Colors.orange.shade600 : Colors.green.shade600,
),
),
SizedBox(width: 8.w),
Icon(
Icons.keyboard_arrow_down,
color: Colors.red.shade600,
size: 30.w,
),
],
),
),
SizedBox(height: 10.h),
// Slot Machine Display
Expanded( Expanded(
child: Container( child: Container(
width: 280.w, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey.shade800, gradient: LinearGradient(
borderRadius: BorderRadius.circular(16.r), colors: [
Colors.grey.shade900,
Colors.grey.shade800,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.circular(20.r),
border: Border.all(color: Colors.orange.shade400, width: 3.w), border: Border.all(color: Colors.orange.shade400, width: 3.w),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.3), color: Colors.black.withOpacity(0.35),
blurRadius: 15.r, blurRadius: 18.r,
offset: Offset(0, 8.h), offset: Offset(0, 10.h),
), ),
], ],
), ),
child: Column( child: Container(
children: [ margin: EdgeInsets.all(18.w),
// Slot Windows decoration: BoxDecoration(
Expanded( color: Colors.black,
child: Container( borderRadius: BorderRadius.circular(12.r),
margin: EdgeInsets.all(16.w), ),
decoration: BoxDecoration( child: Stack(
color: Colors.black, children: [
borderRadius: BorderRadius.circular(8.r), AnimatedBuilder(
), animation: _slotAnimation,
child: Stack( builder: (context, child) {
children: [ return Column(
// Animated names children: List.generate(7, (index) {
AnimatedBuilder( final displayIndex = index < _displayNames.length ? index : 0;
animation: _slotAnimation, final name = _displayNames[displayIndex];
builder: (context, child) { final isWinner = _isComplete && index == 3;
return Column( final isCenterHighlight = _isAnimating && index == 3;
children: List.generate(7, (index) {
final displayIndex = index < _displayNames.length ? index : 0; return Expanded(
final name = _displayNames[displayIndex]; child: AnimatedBuilder(
final isWinner = _isComplete && index == 3; // Center position (middle of 7) animation: _pulseAnimation,
final isCenterHighlight = _isAnimating && index == 3; // Always highlight center builder: (context, child) {
final double scale = isWinner || isCenterHighlight ? 1.08 : 1.0;
return Expanded( final double fontSize = isWinner
child: AnimatedBuilder( ? 24.sp
animation: _pulseAnimation, : isCenterHighlight
builder: (context, child) { ? 20.sp
return AnimatedContainer( : 18.sp;
duration: const Duration(milliseconds: 200), final FontWeight weight = isWinner
curve: Curves.easeInOut, ? FontWeight.w900
transform: Matrix4.identity() : isCenterHighlight
..scale(isCenterHighlight || isWinner ? 1.05 : 1.0), ? FontWeight.w800
child: Container( : FontWeight.w700;
width: double.infinity, final List<Color> colors = isWinner
margin: EdgeInsets.symmetric(vertical: 2.h, horizontal: 4.w), ? [Colors.green.shade500, Colors.green.shade600]
decoration: BoxDecoration( : isCenterHighlight
gradient: isWinner ? [Colors.deepPurple.shade500, Colors.deepPurple.shade700]
? LinearGradient( : [Colors.blueGrey.shade700, Colors.blueGrey.shade900];
colors: [
Colors.green.shade600, return AnimatedContainer(
Colors.green.shade700, duration: const Duration(milliseconds: 220),
], curve: Curves.easeInOut,
) transform: Matrix4.identity()..scale(scale),
: isCenterHighlight child: Container(
? LinearGradient( width: double.infinity,
colors: [ margin: EdgeInsets.symmetric(
Colors.orange.shade600, vertical: 6.h,
Colors.red.shade600, horizontal: 12.w,
], ),
) decoration: BoxDecoration(
: LinearGradient( gradient: LinearGradient(
colors: [ colors: colors,
Colors.blue.shade700, begin: Alignment.topLeft,
Colors.blue.shade800, end: Alignment.bottomRight,
], ),
), borderRadius: BorderRadius.circular(10.r),
borderRadius: BorderRadius.circular(8.r), border: Border.all(
border: Border.all( color: Colors.white.withOpacity(isWinner || isCenterHighlight ? 0.7 : 0.15),
color: isWinner || isCenterHighlight width: isWinner || isCenterHighlight ? 2.w : 1.w,
? Colors.white.withOpacity(0.6) ),
: Colors.white.withOpacity(0.1), boxShadow: [
width: isWinner || isCenterHighlight ? 2.w : 1.w, BoxShadow(
), color: Colors.black.withOpacity(0.4),
boxShadow: isWinner blurRadius: isWinner ? 14.r : 6.r,
? [ offset: Offset(0, 3.h),
BoxShadow(
color: Colors.green.shade300,
blurRadius: 12.r,
spreadRadius: 3.r,
),
]
: isCenterHighlight
? [
BoxShadow(
color: Colors.orange.shade300.withOpacity(0.6),
blurRadius: 8.r,
spreadRadius: 2.r,
),
]
: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 4.r,
offset: Offset(0, 2.h),
),
],
),
child: Center(
child: Text(
name.length > 15 ? '${name.substring(0, 15)}...' : name,
style: TextStyle(
fontSize: isWinner || isCenterHighlight ? 16.sp : 13.sp,
fontWeight: isWinner || isCenterHighlight
? FontWeight.w900
: FontWeight.w600,
color: Colors.white,
letterSpacing: 0.5,
shadows: [
Shadow(
color: Colors.black.withOpacity(0.5),
blurRadius: 3.r,
offset: Offset(1, 1),
),
],
),
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
), ),
); ],
}, ),
), child: Center(
); child: Text(
}), name.length > 22 ? '${name.substring(0, 22)}' : name,
); style: TextStyle(
}, fontSize: fontSize,
), fontWeight: weight,
color: Colors.white,
// Winner highlight letterSpacing: 0.6,
if (_isComplete) shadows: [
Positioned( Shadow(
top: 0, color: Colors.black.withOpacity(0.5),
left: 0, blurRadius: 4.r,
right: 0, offset: Offset(1.5, 1.5),
child: Container( ),
height: 60.h, ],
decoration: BoxDecoration( ),
color: Colors.green.shade400.withOpacity(0.3), textAlign: TextAlign.center,
borderRadius: BorderRadius.circular(4.r), maxLines: 1,
), overflow: TextOverflow.ellipsis,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.emoji_events, color: Colors.yellow, size: 24.w),
SizedBox(width: 8.w),
Text(
'WINNER!',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
color: Colors.yellow,
), ),
), ),
], ),
), );
},
),
);
}),
);
},
),
if (_isComplete)
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
height: 70.h,
decoration: BoxDecoration(
color: Colors.green.shade400.withOpacity(0.35),
borderRadius: BorderRadius.circular(6.r),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.emoji_events, color: Colors.yellowAccent, size: 26.w),
SizedBox(width: 10.w),
Text(
'WINNER',
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.w900,
color: Colors.yellowAccent,
), ),
), ),
), ],
], ),
),
), ),
), ],
), ),
// Slot Machine Controls
Container(
padding: EdgeInsets.all(16.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: Colors.red.shade600,
shape: BoxShape.circle,
),
child: Icon(Icons.stop, color: Colors.white, size: 20.w),
),
Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: Colors.green.shade600,
shape: BoxShape.circle,
),
child: Icon(Icons.play_arrow, color: Colors.white, size: 20.w),
),
Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: Colors.blue.shade600,
shape: BoxShape.circle,
),
child: Icon(Icons.pause, color: Colors.white, size: 20.w),
),
],
),
),
],
), ),
), ),
), ),
SizedBox(height: 16.h),
// Status if (_isComplete && winnerName != null)
SizedBox(height: 20.h), Container(
if (_isAnimating) padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h),
Text( decoration: BoxDecoration(
'Slot machine spinning...', color: Colors.green.shade50,
style: TextStyle( borderRadius: BorderRadius.circular(12.r),
fontSize: 16.sp,
color: Colors.orange.shade600,
), ),
) child: Row(
else if (_isComplete) mainAxisAlignment: MainAxisAlignment.center,
Text( children: [
'Winner: ${widget.members.firstWhere((m) => m['id'] == _winnerId)['name']}', Icon(Icons.emoji_events, color: Colors.green.shade600, size: 20.w),
style: TextStyle( SizedBox(width: 8.w),
fontSize: 16.sp, Flexible(
color: Colors.green.shade600, child: Text(
fontWeight: FontWeight.bold, winnerName,
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w700,
color: Colors.green.shade700,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
), ),
), ),
], ],

View File

@ -1,16 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'animated_draw_wheel.dart';
import 'alternative_draw_animations.dart'; import 'alternative_draw_animations.dart';
import 'particle_draw_animation.dart';
enum DrawAnimationType {
spinningWheel,
cardFlip,
slotMachine,
numberRoulette,
particleSystem,
}
class DrawAnimationSelector extends StatefulWidget { class DrawAnimationSelector extends StatefulWidget {
final List<Map<String, dynamic>> members; final List<Map<String, dynamic>> members;
@ -35,28 +25,11 @@ class DrawAnimationSelector extends StatefulWidget {
} }
class _DrawAnimationSelectorState extends State<DrawAnimationSelector> { class _DrawAnimationSelectorState extends State<DrawAnimationSelector> {
DrawAnimationType _selectedAnimation = DrawAnimationType.cardFlip;
bool _isDrawStarted = false; bool _isDrawStarted = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Auto-select best animation based on member count
_selectBestAnimation();
}
void _selectBestAnimation() {
final memberCount = widget.members.length;
if (memberCount <= 8) {
_selectedAnimation = DrawAnimationType.spinningWheel;
} else if (memberCount <= 20) {
_selectedAnimation = DrawAnimationType.cardFlip;
} else if (memberCount <= 50) {
_selectedAnimation = DrawAnimationType.slotMachine;
} else {
_selectedAnimation = DrawAnimationType.particleSystem;
}
} }
void _startDraw() { void _startDraw() {
@ -73,7 +46,7 @@ class _DrawAnimationSelectorState extends State<DrawAnimationSelector> {
Widget _buildAnimationSelector() { Widget _buildAnimationSelector() {
return Container( return Container(
padding: EdgeInsets.all(20.w), padding: EdgeInsets.all(24.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
@ -86,33 +59,58 @@ class _DrawAnimationSelectorState extends State<DrawAnimationSelector> {
], ],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text( Container(
'Choose Draw Animation', width: 72.w,
style: TextStyle( height: 72.w,
fontSize: 20.sp, decoration: BoxDecoration(
fontWeight: FontWeight.bold, color: Colors.purple.shade50,
color: Colors.grey.shade800, borderRadius: BorderRadius.circular(20.r),
),
child: Icon(
Icons.casino,
color: Colors.purple.shade600,
size: 36.w,
), ),
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Text( Text(
'Members: ${widget.members.length}', 'Slot Machine Draw',
style: TextStyle(
fontSize: 22.sp,
fontWeight: FontWeight.w700,
color: Colors.grey.shade800,
),
textAlign: TextAlign.center,
),
SizedBox(height: 12.h),
Text(
'Our signature animation for dramatic, high-energy winner reveals.',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: Colors.grey.shade600, color: Colors.grey.shade600,
height: 1.4,
),
textAlign: TextAlign.center,
),
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h),
decoration: BoxDecoration(
color: Colors.purple.shade50,
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
'Members in draw: ${widget.members.length}',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
color: Colors.purple.shade600,
),
), ),
), ),
SizedBox(height: 20.h),
// Animation Options
...DrawAnimationType.values.map((type) => _buildAnimationOption(type)),
SizedBox(height: 24.h), SizedBox(height: 24.h),
// Start Button
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(
@ -131,7 +129,7 @@ class _DrawAnimationSelectorState extends State<DrawAnimationSelector> {
Icon(Icons.play_arrow, size: 20.w), Icon(Icons.play_arrow, size: 20.w),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'Start ${_getAnimationName(_selectedAnimation)}', 'Start Slot Machine',
style: TextStyle(fontSize: 16.sp), style: TextStyle(fontSize: 16.sp),
), ),
], ],
@ -143,221 +141,6 @@ class _DrawAnimationSelectorState extends State<DrawAnimationSelector> {
); );
} }
Widget _buildAnimationOption(DrawAnimationType type) {
final isSelected = _selectedAnimation == type;
final isRecommended = _isRecommendedAnimation(type);
return Container(
margin: EdgeInsets.only(bottom: 12.h),
child: InkWell(
onTap: () {
setState(() {
_selectedAnimation = type;
});
},
borderRadius: BorderRadius.circular(12.r),
child: Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isSelected ? Colors.purple.shade50 : Colors.grey.shade50,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isSelected ? Colors.purple.shade300 : Colors.grey.shade300,
width: 2.w,
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 24.w,
height: 24.w,
margin: EdgeInsets.only(top: 2.h),
decoration: BoxDecoration(
color: isSelected ? Colors.purple.shade600 : Colors.grey.shade400,
shape: BoxShape.circle,
),
child: isSelected
? Icon(Icons.check, color: Colors.white, size: 16.w)
: null,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Text(
_getAnimationName(type),
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: isSelected ? Colors.purple.shade800 : Colors.grey.shade800,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (isRecommended) ...[
SizedBox(width: 6.w),
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h),
decoration: BoxDecoration(
color: Colors.green.shade100,
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
'Recommended',
style: TextStyle(
fontSize: 10.sp,
color: Colors.green.shade700,
fontWeight: FontWeight.w500,
),
),
),
],
],
),
SizedBox(height: 6.h),
Text(
_getAnimationDescription(type),
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey.shade600,
height: 1.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
SizedBox(width: 12.w),
Icon(
_getAnimationIcon(type),
color: isSelected ? Colors.purple.shade600 : Colors.grey.shade400,
size: 28.w,
),
],
),
),
),
);
}
bool _isRecommendedAnimation(DrawAnimationType type) {
final memberCount = widget.members.length;
switch (type) {
case DrawAnimationType.spinningWheel:
return memberCount <= 8;
case DrawAnimationType.cardFlip:
return memberCount > 8 && memberCount <= 20;
case DrawAnimationType.slotMachine:
return memberCount > 20 && memberCount <= 50;
case DrawAnimationType.numberRoulette:
return memberCount > 20 && memberCount <= 100;
case DrawAnimationType.particleSystem:
return memberCount > 50;
}
}
String _getAnimationName(DrawAnimationType type) {
switch (type) {
case DrawAnimationType.spinningWheel:
return 'Spinning Wheel';
case DrawAnimationType.cardFlip:
return 'Card Flip';
case DrawAnimationType.slotMachine:
return 'Slot Machine';
case DrawAnimationType.numberRoulette:
return 'Number Roulette';
case DrawAnimationType.particleSystem:
return 'Particle System';
}
}
String _getAnimationDescription(DrawAnimationType type) {
switch (type) {
case DrawAnimationType.spinningWheel:
return 'Classic spinning wheel - Best for small groups (≤8 members)';
case DrawAnimationType.cardFlip:
return 'Cards flipping rapidly - Great for medium groups (9-20 members)';
case DrawAnimationType.slotMachine:
return 'Slot machine style - Perfect for large groups (21-50 members)';
case DrawAnimationType.numberRoulette:
return 'Number roulette wheel - Good for very large groups (21-100 members)';
case DrawAnimationType.particleSystem:
return 'Particle effects - Best for massive groups (50+ members)';
}
}
IconData _getAnimationIcon(DrawAnimationType type) {
switch (type) {
case DrawAnimationType.spinningWheel:
return Icons.casino;
case DrawAnimationType.cardFlip:
return Icons.style;
case DrawAnimationType.slotMachine:
return Icons.games;
case DrawAnimationType.numberRoulette:
return Icons.timeline;
case DrawAnimationType.particleSystem:
return Icons.auto_awesome;
}
}
Widget _buildSelectedAnimation() {
switch (_selectedAnimation) {
case DrawAnimationType.spinningWheel:
return AnimatedDrawWheel(
members: widget.members,
onDrawComplete: widget.onDrawComplete,
serverSeed: widget.serverSeed,
clientSeed: widget.clientSeed,
nonce: widget.nonce,
);
case DrawAnimationType.cardFlip:
return CardFlipDrawAnimation(
members: widget.members,
onDrawComplete: widget.onDrawComplete,
serverSeed: widget.serverSeed,
clientSeed: widget.clientSeed,
nonce: widget.nonce,
animationDuration: widget.animationDuration,
);
case DrawAnimationType.slotMachine:
return SlotMachineDrawAnimation(
members: widget.members,
onDrawComplete: widget.onDrawComplete,
serverSeed: widget.serverSeed,
clientSeed: widget.clientSeed,
nonce: widget.nonce,
animationDuration: widget.animationDuration,
);
case DrawAnimationType.numberRoulette:
return NumberRouletteDrawAnimation(
members: widget.members,
onDrawComplete: widget.onDrawComplete,
serverSeed: widget.serverSeed,
clientSeed: widget.clientSeed,
nonce: widget.nonce,
animationDuration: widget.animationDuration,
);
case DrawAnimationType.particleSystem:
return ParticleDrawAnimation(
members: widget.members,
onDrawComplete: widget.onDrawComplete,
serverSeed: widget.serverSeed,
clientSeed: widget.clientSeed,
nonce: widget.nonce,
animationDuration: widget.animationDuration,
);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
@ -366,7 +149,14 @@ class _DrawAnimationSelectorState extends State<DrawAnimationSelector> {
children: [ children: [
if (!_isDrawStarted) _buildAnimationSelector(), if (!_isDrawStarted) _buildAnimationSelector(),
if (_isDrawStarted) ...[ if (_isDrawStarted) ...[
_buildSelectedAnimation(), SlotMachineDrawAnimation(
members: widget.members,
onDrawComplete: widget.onDrawComplete,
serverSeed: widget.serverSeed,
clientSeed: widget.clientSeed,
nonce: widget.nonce,
animationDuration: widget.animationDuration,
),
SizedBox(height: 20.h), SizedBox(height: 20.h),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
@ -378,9 +168,20 @@ class _DrawAnimationSelectorState extends State<DrawAnimationSelector> {
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
), ),
), ),
child: Text( child: Row(
'Choose Different Animation', mainAxisAlignment: MainAxisAlignment.center,
style: TextStyle(fontSize: 14.sp), children: [
Icon(Icons.refresh, size: 18.w, color: Colors.purple.shade600),
SizedBox(width: 6.w),
Text(
'Spin Again',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: Colors.purple.shade600,
),
),
],
), ),
), ),
), ),