Skip to content

Instantly share code, notes, and snippets.

@gabbygreat
Created December 18, 2024 01:32
Show Gist options
  • Select an option

  • Save gabbygreat/735b5b91a91938fd4a98b05cc302b4c7 to your computer and use it in GitHub Desktop.

Select an option

Save gabbygreat/735b5b91a91938fd4a98b05cc302b4c7 to your computer and use it in GitHub Desktop.
Spin Effect
import 'package:demo/spinwheel_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:go_router/go_router.dart';
void main() {
usePathUrlStrategy();
GoRouter.optionURLReflectsImperativeAPIs = true;
runApp(const MyApp());
}
// Define GoRouter routes.
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const SettingsPage();
}
}
class SettingsPage extends StatefulWidget {
static const name = 'settings';
static const path = '/settings';
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
List<String> data = [];
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Settings'),
),
body: Center(
child: SpinWheelWidget(
preselectedIndex: 4,
onSpinEnd: (p0) {
print(p0);
},
items: [
SpinWheelItem(
color: Colors.purple,
label: 'Hello 0',
),
SpinWheelItem(
color: Colors.blue,
label: 'Hello 1',
),
SpinWheelItem(
color: Colors.green,
label: 'Hello 2',
),
SpinWheelItem(
color: Colors.brown,
label: 'Hello 3',
),
SpinWheelItem(
color: Colors.yellow,
label: 'Hello 4',
),
],
),
),
),
);
}
}
import 'dart:math' as math;
import 'package:flutter/material.dart';
extension AngleConversion on num {
/// Converts degrees to radians
double toRadians() => this * (math.pi / 180.0);
}
class SpinWheelWidget extends StatefulWidget {
final List<SpinWheelItem> items;
final Function(SpinWheelItem) onSpinEnd;
final int preselectedIndex;
final double wheelSize;
// final SpinWheelStateProvider spinWheelState;
const SpinWheelWidget({
super.key,
required this.items,
required this.onSpinEnd,
required this.preselectedIndex,
this.wheelSize = 300,
// required this.spinWheelState,
}) : assert(preselectedIndex < items.length,
'Preselected index cannot be higher than or equal to the length of items');
@override
State<SpinWheelWidget> createState() => _SpinWheelWidgetState();
}
class _SpinWheelWidgetState extends State<SpinWheelWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation = const AlwaysStoppedAnimation(0.0);
bool isSpinning = false;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 5),
);
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {
isSpinning = false;
});
final selectedItem = widget.items[widget.preselectedIndex];
widget.onSpinEnd(selectedItem);
}
});
}
double get getRandomValue {
int n = widget.items.length;
int desiredValue = widget.preselectedIndex;
if (desiredValue < 0 || desiredValue >= n) {
throw ArgumentError('desiredValue must be between 0 and n-1.');
}
final random = math.Random();
final segment = 360 / n;
// Calculate the start and end of the range for the given desired value
final start = (n - desiredValue - 1) * segment + 1;
final end = (n - desiredValue - 1) * segment + segment - 1;
// Generate a random value (decimal) within the range
return start + random.nextDouble() * (end - start);
}
Future<void> startSpin() async {
int speed = math.Random().nextInt(5) + 5; // creates effect of a longer spin
_animation = Tween<double>(
begin: 0,
end: ((getRandomValue + 360 * speed)).toRadians(),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOutCubic,
));
setState(() {
isSpinning = true;
});
_controller.reset();
_controller.forward();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: isSpinning ? null : startSpin,
child: Stack(
alignment: Alignment.center,
children: [
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.rotate(
angle: _animation.value,
child: Container(
width: widget.wheelSize,
height: widget.wheelSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.black, width: 2),
),
child: CustomPaint(
painter: SpinWheelPainter(
items: widget.items,
),
),
),
);
},
),
const Positioned(
top: -45,
child: Icon(
Icons.arrow_drop_down,
size: 100,
color: Colors.red,
),
),
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class SpinWheelPainter extends CustomPainter {
final List<SpinWheelItem> items;
SpinWheelPainter({required this.items});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
final segmentAngle = 2 * math.pi / items.length;
final sections = 360 / items.length; // degrees
double startAngle = 270.0;
for (var i = 0; i < items.length; i++) {
final paint = Paint()..color = items[i].color;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
startAngle.toRadians(),
segmentAngle,
true,
paint,
);
final textAngle = startAngle.toRadians() + (segmentAngle / 2);
final textRadius = radius * 0.7;
startAngle += sections;
TextPainter textPainter = TextPainter(
text: TextSpan(
text: '${items[i].label} {SEG $i}',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
canvas.save();
canvas.translate(
center.dx + textRadius * math.cos(textAngle),
center.dy + textRadius * math.sin(textAngle),
);
canvas.rotate(textAngle + math.pi / 2);
textPainter.paint(
canvas,
Offset(-textPainter.width / 2, -textPainter.height / 2),
);
canvas.restore();
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
class SpinWheelItem {
final String label;
final Color color;
final dynamic data;
SpinWheelItem({
required this.label,
required this.color,
this.data,
});
@override
String toString() {
return """
SpinWheelItem(
label: $label,
color: $color,
data: $data,
)""";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment