Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Forked from tarek360/spinner_001.dart
Last active April 16, 2020 17:36
Show Gist options
  • Save slightfoot/a868a1d0fcd2d28b13d08284a0c8aed1 to your computer and use it in GitHub Desktop.
Save slightfoot/a868a1d0fcd2d28b13d08284a0c8aed1 to your computer and use it in GitHub Desktop.
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Spinner(
radius: 16.0,
),
),
),
);
}
}
class Spinner extends StatefulWidget {
const Spinner({
Key key,
this.radius = 12.0,
}) : assert(radius != null),
super(key: key);
final double radius;
@override
_SpinnerState createState() => _SpinnerState();
}
class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
AnimationController _controller;
static final _colors = <Color>[
Color(0xFFE6194B),
Color(0xFFF58231),
Color(0xFFBFEF45),
Color(0xFFF032E6),
Color(0xFF42D4F4),
];
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 3000),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final widthCircles = _colors.length * widget.radius * 2.0;
final widthSpacing = (_colors.length - 1) * widget.radius;
return RepaintBoundary(
child: CustomPaint(
painter: _SpinnerPainter(
animation: _controller,
colors: _colors,
radius: widget.radius,
),
size: Size(widthCircles + widthSpacing, widget.radius * 6),
willChange: true,
),
);
}
}
class _SpinnerPainter extends CustomPainter {
_SpinnerPainter({
this.animation,
this.colors,
this.radius = 12.0,
}) : super(repaint: animation);
final Animation<double> animation;
final List<Color> colors;
final double radius;
static Curve _curveOfCurve = Cubic(.51, .23, .8, .38);
@override
void paint(Canvas canvas, Size size) {
final range = animation.value * colors.length;
final numerator = range.truncate();
final fractional = range - numerator;
final centerX = size.width / 2.0;
final baselineY = size.height - radius;
for (int i = 1; i < colors.length; i++) {
final offset = Offset((radius + i * radius * 3) - (radius * 3 * fractional), baselineY);
canvas.drawCircle(offset, radius, Paint()..color = colors[(i + numerator) % colors.length]);
}
final time = _curveOfCurve.transform(fractional);
final offset = bezierPoint(radius, baselineY, centerX, -radius * 2.5, size.width - radius, baselineY, time);
canvas.drawCircle(offset, radius, Paint()..color = colors[numerator % colors.length]);
}
@override
bool shouldRepaint(_SpinnerPainter old) =>
this.animation != old.animation || this.colors != old.colors || this.radius != old.radius;
static Offset bezierPoint(
double startX, double startY, double controlX, double controlY, double endX, double endY, double time) {
// Bézier curve calculation https://en.wikipedia.org/wiki/B%C3%A9zier_curve
return Offset(
(1 - time) * (1 - time) * startX + 2 * (1 - time) * time * controlX + time * time * endX,
(1 - time) * (1 - time) * startY + 2 * (1 - time) * time * controlY + time * time * endY,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment