Import Required Libraries
import 'dart:math';
import 'package:flutter/material.dart';
Define the Main Widget
Define the SpinAndWin widget as a StatefulWidget, which will display the spin wheel and handle interactions.
class SpinAndWin extends StatefulWidget {
const SpinAndWin({Key? key}) : super(key: key);
@override
State<SpinAndWin> createState() => _SpinAndWinState();
}
Initialize State and Animation Controller
Create the state class _SpinAndWinState and initialize an animation controller, which will manage the spinning effect.
class _SpinAndWinState extends State<SpinAndWin> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
final Random _random = Random();
int _totalPoints = 0;
int _currentPoints = 0;
bool _isSpinning = false;
Define the Wheel Segments and Initialize Animation Controller
Define the segments of the wheel, each with a color and points. Also, set up the animation controller and handle the end of the spin.
final List<Map<String, dynamic>> segments = [
{'points': 10, 'color': Colors.red},
{'points': 20, 'color': Colors.orange},
// Add remaining segments here
];
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 5),
);
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {
_isSpinning = false;
_calculatePoints();
});
}
});
}
Implement Points Calculation
Write a function _calculatePoints() to determine how many points the user wins based on where the wheel stops.
void _calculatePoints() {
final double finalAngle = _animation.value % (2 * pi);
final double normalizedAngle = finalAngle >= 0 ? finalAngle : (2 * pi + finalAngle);
final int segmentIndex = (normalizedAngle / (2 * pi) * segments.length).floor() % segments.length;
final int wonPoints = segments[segmentIndex]['points'] as int;
setState(() {
_currentPoints = wonPoints;
_totalPoints += wonPoints;
});
_showWinDialog(wonPoints);
}
Display Win Dialog
Create a _showWinDialog() function to show an alert dialog when the user wins points.
void _showWinDialog(int points) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('🎉 Congratulations! 🎉'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('You won $points points!', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
Text('Total Points: $_totalPoints'),
],
),
actions: [
TextButton(
child: const Text('Spin Again'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Create the Spin Function
Define _spinWheel() to initiate the spinning, setting the animation to rotate by a random number of spins.
void _spinWheel() {
if (_isSpinning) return;
setState(() {
_isSpinning = true;
_currentPoints = 0;
});
final double spins = 3 + _random.nextDouble() * 2;
final double angle = spins * 2 * pi;
_animation = Tween<double>(begin: 0, end: angle).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOutCirc),
);
_controller.reset();
_controller.forward();
}
Build the UI and Main Function
Build the Scaffold widget UI for the spin wheel and add the WheelPainter class to paint each segment.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.blue.shade900, Colors.blue.shade600]),
),
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Spin To Win Daily Earn', style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.yellow)),
const SizedBox(height: 20),
Stack(alignment: Alignment.center, children: [
AnimatedBuilder(
animation: _controller,
builder: (_, child) {
return Transform.rotate(
angle: _controller.isAnimating ? _animation.value : 0,
child: Container(width: 300, height: 300, decoration: const BoxDecoration(shape: BoxShape.circle), child: CustomPaint(painter: WheelPainter(segments: segments))),
);
},
),
Container(width: 20, height: 20, decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle)),
]),
const SizedBox(height: 40),
ElevatedButton(
onPressed: _isSpinning ? null : _spinWheel,
style: ElevatedButton.styleFrom(backgroundColor: Colors.redAccent, padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25))),
child: Text(_isSpinning ? 'Spinning...' : 'SPIN!', style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
),
const SizedBox(height: 20),
Text('Total Points: $_totalPoints', style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.yellow)),
],
),
),
),
);
}
}
class WheelPainter extends CustomPainter {
final List<Map<String, dynamic>> segments;
WheelPainter({required this.segments});
@override
void paint(Canvas canvas, Size size) {
final double radius = size.width / 2;
final Offset center = Offset(radius, radius);
final double segmentAngle = 2 * pi / segments.length;
for (int i = 0; i < segments.length; i++) {
final Paint paint = Paint()..color = segments[i]['color'];
final Path path = Path()..moveTo(center.dx, center.dy)..arcTo(Rect.fromCircle(center: center, radius: radius), i * segmentAngle, segmentAngle, true)..close();
canvas.drawPath(path, paint);
final double textAngle = i * segmentAngle + segmentAngle / 2;
final double textRadius = radius * 0.7;
final Offset textCenter = Offset(center.dx + textRadius * cos(textAngle), center.dy + textRadius * sin(textAngle));
final TextPainter textPainter = TextPainter(text: TextSpan(text: '${segments[i]['points']}', style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)), textDirection: TextDirection.ltr)..layout();
textPainter.paint(canvas, textCenter.translate(-textPainter.width / 2, -textPainter.height / 2));
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
void main() {
runApp(const MaterialApp(home: SpinAndWin()));
}