Last active
May 27, 2020 01:37
-
-
Save tarek360/dd64cc40af2acf3bbd9bc9b464289849 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:ui'; | |
import 'package:flutter/material.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
home: Scaffold( | |
backgroundColor: Colors.black, | |
body: Center( | |
child: SizedBox( | |
width: 200, | |
height: 200, | |
child: Spinner(), | |
), | |
), | |
), | |
); | |
} | |
} | |
class Spinner extends StatefulWidget { | |
@override | |
_SpinnerState createState() => _SpinnerState(); | |
} | |
class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin { | |
AnimationController _controller; | |
Offset _c1 = Offset(1.0, 0.5); | |
Offset _c2 = Offset(0.75, 0.5); | |
Offset _c3 = Offset(0.5, 0.5); | |
Offset _c4 = Offset(0.25, 0.5); | |
Offset _c5 = Offset(0.0, 0.5); | |
@override | |
void initState() { | |
super.initState(); | |
_controller = AnimationController( | |
duration: Duration(milliseconds: 3000), vsync: this); | |
AnimationSet _animationSet = AnimationSet(_controller); | |
_controller.addListener(() { | |
if (_controller.value < 0.8) { | |
_c1 = _animationSet.c1p1.value; | |
} else { | |
_c1 = _animationSet.c1p2.value; | |
} | |
if (_controller.value < 0.6) { | |
_c2 = _animationSet.c2p1.value; | |
} else if (_controller.value < 0.8) { | |
_c2 = _animationSet.c2p2.value; | |
} else { | |
_c2 = _animationSet.c2p3.value; | |
} | |
if (_controller.value < 0.4) { | |
_c3 = _animationSet.c3p1.value; | |
} else if (_controller.value < 0.6) { | |
_c3 = _animationSet.c3p2.value; | |
} else { | |
_c3 = _animationSet.c3p3.value; | |
} | |
if (_controller.value < 0.2) { | |
_c4 = _animationSet.c4p1.value; | |
} else if (_controller.value < 0.4) { | |
_c4 = _animationSet.c4p2.value; | |
} else { | |
_c4 = _animationSet.c4p3.value; | |
} | |
if (_controller.value < 0.2) { | |
_c5 = _animationSet.c5p1.value; | |
} else { | |
_c5 = _animationSet.c5p2.value; | |
} | |
setState(() {}); | |
}); | |
_controller.repeat(); | |
} | |
@override | |
void dispose() { | |
_controller.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return AspectRatio( | |
aspectRatio: 1, | |
child: CustomPaint( | |
size: Size(double.infinity, double.infinity), | |
painter: _SpinnerPainter(_c1, _c2, _c3, _c4, _c5), | |
), | |
); | |
} | |
} | |
class AnimationSet { | |
static Curve _curveOfCurve = Cubic(.51,.23,.8,.38); | |
AnimationSet(this.controller) | |
: | |
// ********** C1 ********** | |
c1p1 = _OffsetTween(Offset(1.0, 0.5), Offset(0.0, 0.5)) | |
.animate(CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.0, | |
0.8, | |
curve: Curves.linear, | |
), | |
)), | |
c1p2 = _CurveTween().animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.8, | |
1.0, | |
curve: _curveOfCurve, | |
), | |
), | |
), | |
// ********** C2 ********** | |
c2p1 = _OffsetTween(Offset(0.75, 0.5), Offset(0.0, 0.5)) | |
.animate(CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.0, | |
0.6, | |
curve: Curves.linear, | |
), | |
)), | |
c2p2 = _CurveTween().animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.6, | |
0.8, | |
curve: _curveOfCurve, | |
), | |
), | |
), | |
c2p3 = _OffsetTween(Offset(1.0, 0.5), Offset(0.75, 0.5)).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.8, | |
1.0, | |
curve: Curves.linear, | |
), | |
), | |
), | |
// ********** C3 ********** | |
c3p1 = _OffsetTween(Offset(0.5, 0.5), Offset(0.0, 0.5)) | |
.animate(CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.0, | |
0.4, | |
curve: Curves.linear, | |
), | |
)), | |
c3p2 = _CurveTween().animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.4, | |
0.6, | |
curve: _curveOfCurve, | |
), | |
), | |
), | |
c3p3 = _OffsetTween(Offset(1.0, 0.5), Offset(0.5, 0.5)).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.6, | |
1.0, | |
curve: Curves.linear, | |
), | |
), | |
), | |
// ********** C4 ********** | |
c4p1 = _OffsetTween(Offset(0.25, 0.5), Offset(0.0, 0.5)) | |
.animate(CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.0, | |
0.2, | |
curve: Curves.linear, | |
), | |
)), | |
c4p2 = _CurveTween().animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.2, | |
0.4, | |
curve: _curveOfCurve, | |
), | |
), | |
), | |
c4p3 = _OffsetTween(Offset(1.0, 0.5), Offset(0.25, 0.5)).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.4, | |
1.0, | |
curve: Curves.linear, | |
), | |
), | |
), | |
// ********** C5 ********** | |
c5p1 = _CurveTween().animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.0, | |
0.2, | |
curve: _curveOfCurve, | |
), | |
), | |
), | |
c5p2 = _OffsetTween(Offset(1.0, 0.5), Offset(0.0, 0.5)).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: Interval( | |
0.2, | |
1.0, | |
curve: Curves.linear, | |
), | |
), | |
); | |
final AnimationController controller; | |
final Animation<Offset> c1p1; | |
final Animation<Offset> c1p2; | |
final Animation<Offset> c2p1; | |
final Animation<Offset> c2p2; | |
final Animation<Offset> c2p3; | |
final Animation<Offset> c3p1; | |
final Animation<Offset> c3p2; | |
final Animation<Offset> c3p3; | |
final Animation<Offset> c4p1; | |
final Animation<Offset> c4p2; | |
final Animation<Offset> c4p3; | |
final Animation<Offset> c5p1; | |
final Animation<Offset> c5p2; | |
} | |
class _CurveTween extends Tween<Offset> { | |
static Rect _bounds = Rect.fromLTWH(0, 0, 1, 1); | |
PathMetric metric = (Path() | |
..moveTo(_bounds.centerLeft.dx, _bounds.centerLeft.dy) | |
..quadraticBezierTo(_bounds.topCenter.dx, _bounds.topCenter.dy, | |
_bounds.centerRight.dx, _bounds.centerRight.dy)) | |
.computeMetrics() | |
.first; | |
_CurveTween() : super(begin: _bounds.centerLeft, end: _bounds.centerRight); | |
@override | |
Offset lerp(double time) { | |
return metric.getTangentForOffset(time * metric.length).position; | |
} | |
} | |
class _OffsetTween extends Tween<Offset> { | |
_OffsetTween(Offset begin, Offset end) : super(begin: begin, end: end); | |
@override | |
Offset lerp(double t) { | |
return Offset.lerp(begin, end, t); | |
} | |
} | |
class _SpinnerPainter extends CustomPainter { | |
static Color _red = Color(0xFFe6194B); | |
static Color _orange = Color(0xFFf58231); | |
static Color _lime = Color(0xFFbfef45); | |
static Color _magenta = Color(0xFFf032e6); | |
static Color _cyan = Color(0xFF42d4f4); | |
final Offset circle1; | |
final Offset circle2; | |
final Offset circle3; | |
final Offset circle4; | |
final Offset circle5; | |
_SpinnerPainter( | |
this.circle1, | |
this.circle2, | |
this.circle3, | |
this.circle4, | |
this.circle5, | |
); | |
@override | |
void paint(Canvas canvas, Size size) { | |
final radius = size.width * 0.08; | |
canvas.drawCircle(_map(circle1, size), radius, Paint()..color = _cyan); | |
canvas.drawCircle(_map(circle2, size), radius, Paint()..color = _magenta); | |
canvas.drawCircle(_map(circle3, size), radius, Paint()..color = _lime); | |
canvas.drawCircle(_map(circle4, size), radius, Paint()..color = _orange); | |
canvas.drawCircle(_map(circle5, size), radius, Paint()..color = _red); | |
} | |
Offset _map(Offset offset, Size size) { | |
return Offset(offset.dx * size.width, offset.dy * size.height); | |
} | |
@override | |
bool shouldRepaint(_SpinnerPainter old) => | |
this.circle1 != old.circle1 || | |
this.circle2 != old.circle2 || | |
this.circle3 != old.circle3 || | |
this.circle4 != old.circle4 || | |
this.circle5 != old.circle5; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment