diff --git a/example/android/app/src/main/gen/com/example/example/BuildConfig.java b/example/android/app/src/main/gen/com/example/example/BuildConfig.java new file mode 100644 index 0000000..a0c467d --- /dev/null +++ b/example/android/app/src/main/gen/com/example/example/BuildConfig.java @@ -0,0 +1,8 @@ +/*___Generated_by_IDEA___*/ + +package com.example.example; + +/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */ +public final class BuildConfig { + public final static boolean DEBUG = Boolean.parseBoolean(null); +} \ No newline at end of file diff --git a/example/android/app/src/main/gen/com/example/example/Manifest.java b/example/android/app/src/main/gen/com/example/example/Manifest.java new file mode 100644 index 0000000..d17646d --- /dev/null +++ b/example/android/app/src/main/gen/com/example/example/Manifest.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package com.example.example; + +/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */ +public final class Manifest { +} \ No newline at end of file diff --git a/example/android/app/src/main/gen/com/example/example/R.java b/example/android/app/src/main/gen/com/example/example/R.java new file mode 100644 index 0000000..015570b --- /dev/null +++ b/example/android/app/src/main/gen/com/example/example/R.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package com.example.example; + +/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */ +public final class R { +} \ No newline at end of file diff --git a/flutter_spinkit.iml b/flutter_spinkit.iml index 70b4a9b..cea31c3 100644 --- a/flutter_spinkit.iml +++ b/flutter_spinkit.iml @@ -3,20 +3,15 @@ - - - - - + - - + \ No newline at end of file diff --git a/lib/src/double_bounce.dart b/lib/src/double_bounce.dart index a4d2e62..deef4a0 100644 --- a/lib/src/double_bounce.dart +++ b/lib/src/double_bounce.dart @@ -1,21 +1,19 @@ +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; class SpinKitDoubleBounce extends StatefulWidget { const SpinKitDoubleBounce({ Key key, - this.color, + @required this.color, this.size = 50.0, - this.itemBuilder, this.duration = const Duration(milliseconds: 2000), this.controller, - }) : assert(!(itemBuilder is IndexedWidgetBuilder && color is Color) && !(itemBuilder == null && color == null), - 'You should specify either a itemBuilder or a color'), + }) : assert(color != null), assert(size != null), super(key: key); final Color color; final double size; - final IndexedWidgetBuilder itemBuilder; final Duration duration; final AnimationController controller; @@ -32,9 +30,9 @@ class _SpinKitDoubleBounceState extends State with SingleTi super.initState(); _controller = (widget.controller ?? AnimationController(vsync: this, duration: widget.duration)) - ..addListener(() => setState(() {})) ..repeat(reverse: true); - _animation = Tween(begin: -1.0, end: 1.0).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); + _animation = + Tween(begin: -1.0, end: 1.0).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); } @override @@ -46,18 +44,54 @@ class _SpinKitDoubleBounceState extends State with SingleTi @override Widget build(BuildContext context) { return Center( - child: Stack( - children: List.generate(2, (i) { - return Transform.scale( - scale: (1.0 - i - _animation.value.abs()).abs(), - child: SizedBox.fromSize(size: Size.square(widget.size), child: _itemBuilder(i)), + child: AnimatedBuilder( + animation: _animation, + builder: (context, _) { + return CustomPaint( + size: Size.square(widget.size), + painter: DoubleBouncePainter( + color: widget.color, + opacity: 0.6, + doubleBounceSize: _animation.value.abs() * widget.size, + ), ); - }), + }, ), ); } +} + +class DoubleBouncePainter extends CustomPainter { + DoubleBouncePainter({ + @required this.doubleBounceSize, + @required double opacity, + @required Color color, + }) : _doubleBouncePaint = Paint() + ..color = color.withOpacity(opacity) + ..style = PaintingStyle.fill + ..isAntiAlias = true; + + final double doubleBounceSize; + final Paint _doubleBouncePaint; + + @override + void paint(Canvas canvas, Size size) { + final centerX = size.width / 2.0; + final centerY = size.height / 2.0; + canvas.drawCircle( + Offset(centerX, centerY), + doubleBounceSize / 2, + _doubleBouncePaint, + ); + canvas.drawCircle( + Offset(centerX, centerY), + size.longestSide / 2 - (doubleBounceSize / 2), + _doubleBouncePaint, + ); + } - Widget _itemBuilder(int index) => widget.itemBuilder != null - ? widget.itemBuilder(context, index) - : DecoratedBox(decoration: BoxDecoration(shape: BoxShape.circle, color: widget.color.withOpacity(0.6))); + @override + bool shouldRepaint(DoubleBouncePainter oldDelegate) { + return oldDelegate.doubleBounceSize != doubleBounceSize; + } } diff --git a/lib/src/pulse.dart b/lib/src/pulse.dart index 3750210..e53746b 100644 --- a/lib/src/pulse.dart +++ b/lib/src/pulse.dart @@ -1,21 +1,19 @@ +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; class SpinKitPulse extends StatefulWidget { const SpinKitPulse({ Key key, - this.color, + @required this.color, this.size = 50.0, - this.itemBuilder, this.duration = const Duration(seconds: 1), this.controller, - }) : assert(!(itemBuilder is IndexedWidgetBuilder && color is Color) && !(itemBuilder == null && color == null), - 'You should specify either a itemBuilder or a color'), + }) : assert(color != null), assert(size != null), super(key: key); final Color color; final double size; - final IndexedWidgetBuilder itemBuilder; final Duration duration; final AnimationController controller; @@ -23,7 +21,8 @@ class SpinKitPulse extends StatefulWidget { _SpinKitPulseState createState() => _SpinKitPulseState(); } -class _SpinKitPulseState extends State with SingleTickerProviderStateMixin { +class _SpinKitPulseState extends State + with SingleTickerProviderStateMixin { AnimationController _controller; Animation _animation; @@ -31,9 +30,10 @@ class _SpinKitPulseState extends State with SingleTickerProviderSt void initState() { super.initState(); - _controller = (widget.controller ?? AnimationController(vsync: this, duration: widget.duration)) - ..addListener(() => setState(() {})) + _controller = (widget.controller ?? + AnimationController(vsync: this, duration: widget.duration)) ..repeat(); + _animation = CurveTween(curve: Curves.easeInOut).animate(_controller); } @@ -46,20 +46,48 @@ class _SpinKitPulseState extends State with SingleTickerProviderSt @override Widget build(BuildContext context) { return Center( - child: Opacity( - opacity: 1.0 - _animation.value, - child: Transform.scale( - scale: _animation.value, - child: SizedBox.fromSize( + child: AnimatedBuilder( + animation: _animation, + builder: (context, _) { + return CustomPaint( size: Size.square(widget.size), - child: _itemBuilder(0), - ), - ), + painter: PulsePainter( + color: widget.color, + opacity: 1.0 - _animation.value, + pulseSize: _animation.value * widget.size, + ), + ); + }, ), ); } - - Widget _itemBuilder(int index) => widget.itemBuilder != null - ? widget.itemBuilder(context, index) - : DecoratedBox(decoration: BoxDecoration(shape: BoxShape.circle, color: widget.color)); } + +class PulsePainter extends CustomPainter { + PulsePainter({ + @required this.pulseSize, + @required double opacity, + @required Color color, + }) : _pulsePaint = Paint() + ..color = color.withOpacity(opacity) + ..style = PaintingStyle.fill + ..isAntiAlias = true; + + + + final double pulseSize; + final Paint _pulsePaint; + + + @override + void paint(Canvas canvas, Size size) { + final centerX = size.width / 2.0; + final centerY = size.height / 2.0; + canvas.drawCircle(Offset(centerX, centerY), pulseSize/2, _pulsePaint); + } + + @override + bool shouldRepaint(PulsePainter oldDelegate) { + return oldDelegate.pulseSize != pulseSize; + } +} \ No newline at end of file diff --git a/lib/src/pumping_heart.dart b/lib/src/pumping_heart.dart index c85aa73..0ac1f71 100644 --- a/lib/src/pumping_heart.dart +++ b/lib/src/pumping_heart.dart @@ -1,24 +1,21 @@ import 'dart:math' as math show pow; -import 'package:flutter/material.dart' show Icons; +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; class SpinKitPumpingHeart extends StatefulWidget { const SpinKitPumpingHeart({ Key key, - this.color, + @required this.color, this.size = 50.0, - this.itemBuilder, this.duration = const Duration(milliseconds: 2400), this.controller, - }) : assert(!(itemBuilder is IndexedWidgetBuilder && color is Color) && !(itemBuilder == null && color == null), - 'You should specify either a itemBuilder or a color'), + }) : assert(color != null), assert(size != null), super(key: key); final Color color; final double size; - final IndexedWidgetBuilder itemBuilder; final Duration duration; final AnimationController controller; @@ -34,9 +31,10 @@ class _SpinKitPumpingHeartState extends State with SingleTi void initState() { super.initState(); - _controller = (widget.controller ?? AnimationController(vsync: this, duration: widget.duration))..repeat(); - _animation = Tween(begin: 1.0, end: 1.25) - .animate(CurvedAnimation(parent: _controller, curve: const Interval(0.0, 1.0, curve: SpinKitPumpCurve()))); + _controller = (widget.controller ?? AnimationController(vsync: this, duration: widget.duration)) + ..repeat(); + _animation = Tween(begin: 1.0, end: 1.25).animate( + CurvedAnimation(parent: _controller, curve: const Interval(0.0, 1.0, curve: SpinKitPumpCurve()))); } @override @@ -47,12 +45,66 @@ class _SpinKitPumpingHeartState extends State with SingleTi @override Widget build(BuildContext context) { - return ScaleTransition(scale: _animation, child: _itemBuilder(0)); + return ScaleTransition( + scale: _animation, + child: CustomPaint( + size: Size.square(widget.size), + painter: HeartPainter( + color: widget.color, + ), + ), + ); } +} + +class HeartPainter extends CustomPainter { + HeartPainter({ + @required Color color, + }) : _heartPaint = Paint() + ..color = color + ..style = PaintingStyle.fill + ..isAntiAlias = true; + + final Paint _heartPaint; + + @override + void paint(Canvas canvas, Size size) { + final double width = size.width; + final double height = size.height; + + final Path heartPath = Path(); - Widget _itemBuilder(int index) => widget.itemBuilder != null - ? widget.itemBuilder(context, index) - : Icon(Icons.favorite, color: widget.color, size: widget.size); + //creating heart left half + heartPath.moveTo(0.5 * width, height * 0.15); + heartPath.cubicTo( + 0.2 * width, + height * -0.2, + -0.2 * width, + height * 0.3, + 0.1 * width, + height * 0.6, + ); + heartPath.lineTo(0.5 * width, height); + + //creating heart left half + heartPath.moveTo(0.5 * width, height * 0.15); + heartPath.cubicTo( + 0.8 * width, + height * -0.2, + 1.2 * width, + height * 0.3, + 0.9 * width, + height * 0.6, + ); + heartPath.lineTo(0.5 * width, height); + + canvas.drawPath(heartPath, _heartPaint); + } + + @override + bool shouldRepaint(HeartPainter oldDelegate) { + return oldDelegate._heartPaint != _heartPaint; + } } class SpinKitPumpCurve extends Curve { diff --git a/lib/src/square_circle.dart b/lib/src/square_circle.dart index 1e28469..188efaa 100644 --- a/lib/src/square_circle.dart +++ b/lib/src/square_circle.dart @@ -5,19 +5,16 @@ import 'package:flutter/widgets.dart'; class SpinKitSquareCircle extends StatefulWidget { const SpinKitSquareCircle({ Key key, - this.color, + @required this.color, this.size = 50.0, - this.itemBuilder, this.duration = const Duration(milliseconds: 500), this.controller, - }) : assert(!(itemBuilder is IndexedWidgetBuilder && color is Color) && !(itemBuilder == null && color == null), - 'You should specify either a itemBuilder or a color'), + }) : assert(color != null), assert(size != null), super(key: key); final Color color; final double size; - final IndexedWidgetBuilder itemBuilder; final Duration duration; final AnimationController controller; @@ -35,7 +32,6 @@ class _SpinKitSquareCircleState extends State with SingleTi super.initState(); controller = (widget.controller ?? AnimationController(vsync: this, duration: widget.duration)) - ..addListener(() => setState(() {})) ..repeat(reverse: true); final animation = CurvedAnimation(parent: controller, curve: Curves.easeInOutCubic); animationCurve = Tween(begin: 1.0, end: 0.0).animate(animation); @@ -50,25 +46,68 @@ class _SpinKitSquareCircleState extends State with SingleTi @override Widget build(BuildContext context) { - final sizeValue = widget.size * animationSize.value; - return Center( - child: Transform( - transform: Matrix4.identity()..rotateZ(animationCurve.value * math.pi), - alignment: FractionalOffset.center, - child: SizedBox.fromSize( - size: Size.square(sizeValue), - child: _itembuilder(0, 0.5 * sizeValue * animationCurve.value), - ), + return AnimatedBuilder( + animation: controller, + builder: (context, _) { + final sizeValue = widget.size * animationSize.value; + return Center( + child: CustomPaint( + size: Size.square(sizeValue), + painter: SquareCirclePainter( + rotate: animationCurve.value * math.pi, + borderRadius: 0.5 * sizeValue * animationCurve.value, + color: widget.color, + ), + ), + ); + }); + } +} + +class SquareCirclePainter extends CustomPainter { + SquareCirclePainter({ + @required this.borderRadius, + @required this.rotate, + @required Color color, + }) : _squareCirclePaint = Paint() + ..style = PaintingStyle.fill + ..color = color + ..isAntiAlias = true; + + final double borderRadius; + final double rotate; + final Paint _squareCirclePaint; + + @override + void paint(Canvas canvas, Size size) { + final rRect = RRect.fromRectAndRadius( + Rect.fromLTWH( + 0, + 0, + size.width, + size.height, ), + Radius.circular(borderRadius), ); + + //circle rotation + final double r = math.sqrt(size.width * size.width + size.height * size.height) / 2; + final alpha = math.atan(size.height / size.width); + final beta = alpha + rotate; + final shiftY = r * math.sin(beta); + final shiftX = r * math.cos(beta); + final translateX = size.width / 2 - shiftX; + final translateY = size.height / 2 - shiftY; + + canvas.translate(translateX, translateY); + + canvas.rotate(rotate); + + canvas.drawRRect(rRect, _squareCirclePaint); } - Widget _itembuilder(int index, double curveValue) => widget.itemBuilder != null - ? widget.itemBuilder(context, index) - : DecoratedBox( - decoration: BoxDecoration( - color: widget.color, - borderRadius: BorderRadius.all(Radius.circular(curveValue)), - ), - ); + @override + bool shouldRepaint(SquareCirclePainter oldDelegate) { + return oldDelegate.borderRadius != borderRadius; + } } diff --git a/lib/src/wave.dart b/lib/src/wave.dart index ff02c79..a2bd450 100644 --- a/lib/src/wave.dart +++ b/lib/src/wave.dart @@ -13,7 +13,9 @@ class SpinKitWave extends StatefulWidget { this.itemCount = 5, this.duration = const Duration(milliseconds: 1200), this.controller, - }) : assert(!(itemBuilder is IndexedWidgetBuilder && color is Color) && !(itemBuilder == null && color == null), + }) : assert( + !(itemBuilder is IndexedWidgetBuilder && color is Color) && + !(itemBuilder == null && color == null), 'You should specify either a itemBuilder or a color'), assert(itemCount != null && itemCount >= 2, 'itemCount Cant be less then 2 '), assert(type != null), @@ -59,7 +61,8 @@ class _SpinKitWaveState extends State with SingleTickerProviderStat children: List.generate(_bars.length, (i) { return ScaleYWidget( scaleY: DelayTween(begin: .4, end: 1.0, delay: _bars[i]).animate(_controller), - child: SizedBox.fromSize(size: Size(widget.size / widget.itemCount, widget.size), child: _itemBuilder(i)), + child: SizedBox.fromSize( + size: Size(widget.size / widget.itemCount, widget.size), child: _itemBuilder(i)), ); }), ),