diff --git a/lib/animations/crazy_squares/square_container_animation.dart b/lib/animations/crazy_squares/square_container_animation.dart new file mode 100644 index 0000000..674803f --- /dev/null +++ b/lib/animations/crazy_squares/square_container_animation.dart @@ -0,0 +1,343 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/physics.dart'; +import '../../contributors_card.dart'; + +/// Crazy Square animation +/// This animation performs: +/// - Color Changes +/// - Size Changes +/// - Rotations +/// - New Positions +/// OverView : An automatic play animation that can be used as an +/// Loading animation, when dragged along Y axis it performs Upside Down +/// Spring Animation. +/// TODO : Add Sensors Support +class SquareContainerAnimation extends StatefulWidget { + const SquareContainerAnimation({super.key}); + + @override + State createState() => + _SquareContainerAnimationState(); +} + +class _SquareContainerAnimationState extends State + with TickerProviderStateMixin { + // Controllers + late AnimationController _sizeController; + late AnimationController _rotationController; + late AnimationController _dragAnimateController; + + // Holding Animation Values + late Animation _animationOne; + late Animation _animationTwo; + late Animation _animationThree; + late Animation _animationFour; + late Animation _animationFive; + late Animation _rotationAnimation; + late Animation _dragAnimation; + + Alignment _newOffSetAlignment = Alignment.center; + + @override + void initState() { + super.initState(); + + /// Setting up the Controllers + _rotationController = AnimationController( + vsync: this, + duration: const Duration( + milliseconds: 1500, + ), + ); + + _sizeController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 1500), + ); + + _dragAnimateController = AnimationController.unbounded( + vsync: this, duration: const Duration(milliseconds: 1500)); + + /// Setting up the Animations with respective intervals + _animationOne = Tween( + begin: 100.0, + end: 0.0, + ).animate( + CurvedAnimation( + parent: _sizeController, + curve: const Interval( + 0.0, + 0.200, + curve: Curves.ease, + ), + ), + ); + + _animationTwo = Tween( + begin: 100.0, + end: 0.0, + ).animate( + CurvedAnimation( + parent: _sizeController, + curve: const Interval( + 0.200, + 0.400, + curve: Curves.ease, + ), + ), + ); + + _animationThree = Tween( + begin: 100.0, + end: 0.0, + ).animate( + CurvedAnimation( + parent: _sizeController, + curve: const Interval( + 0.400, + 0.600, + curve: Curves.ease, + ), + ), + ); + + _animationFour = Tween( + begin: 100.0, + end: 0.0, + ).animate( + CurvedAnimation( + parent: _sizeController, + curve: const Interval( + 0.600, + 0.800, + curve: Curves.ease, + ), + ), + ); + + _animationFive = Tween( + begin: 100.0, + end: 0.0, + ).animate( + CurvedAnimation( + parent: _sizeController, + curve: const Interval( + 0.800, + 1, + curve: Curves.ease, + ), + ), + ); + + _rotationAnimation = Tween( + begin: 0, + end: -(pi / 4.0), + ).animate( + CurvedAnimation( + parent: _rotationController, + curve: Curves.bounceOut, + ), + ); + + _playAnimationForward(); + + _dragAnimateController.addListener(() { + setState(() { + _newOffSetAlignment = _dragAnimation.value; + }); + }); + } + + @override + void dispose() { + super.dispose(); + // Disposing Controllers as per the best practices. + _sizeController.dispose(); + _rotationController.dispose(); + _dragAnimateController.dispose(); + } + + @override + Widget build(BuildContext context) { + var size = MediaQuery.of(context).size; + return Scaffold( + body: GestureDetector( + onPanDown: (details) { + _dragAnimateController.stop(); + }, + onPanUpdate: (details) { + setState(() { + _newOffSetAlignment += Alignment( + details.delta.dx / (size.width / 2), + details.delta.dy / (size.width / 2), + ); + }); + }, + onPanEnd: (details) { + _runDraggedReleasedAnimation(details.velocity.pixelsPerSecond, size); + }, + child: Stack( + children: [ + Center( + child: AnimatedBuilder( + animation: _sizeController, + builder: (context, child) { + return Transform( + alignment: Alignment.center, + transform: Matrix4.identity() + // Rotate at Different Axis for more fun animations. + ..rotateY( + _rotationAnimation.value, + ), + child: Align( + alignment: _newOffSetAlignment, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: _animationFive.value, + height: _animationFive.value, + decoration: BoxDecoration( + color: Colors.purpleAccent, + borderRadius: BorderRadius.circular(16), + ), + ), + Container( + width: _animationFour.value, + height: _animationFour.value, + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(16), + ), + ), + Container( + width: _animationThree.value, + height: _animationThree.value, + decoration: BoxDecoration( + color: Colors.green, + borderRadius: BorderRadius.circular(16), + ), + ), + Container( + width: _animationTwo.value, + height: _animationTwo.value, + decoration: BoxDecoration( + color: Colors.yellow, + borderRadius: BorderRadius.circular(16), + ), + ), + Container( + width: _animationOne.value, + height: _animationOne.value, + decoration: BoxDecoration( + color: Colors.lightBlue, + borderRadius: BorderRadius.circular(16), + ), + ), + ], + ), + ), + ); + }, + ), + ), + const Positioned( + left: 0, + bottom: 0, + child: Padding( + // bottom: 32 to avoid parent indicator positioned at bottom + padding: EdgeInsets.only(left: 8, right: 8, bottom: 32), + child: ContributorsCard( + imageUrl: + "https://avatars.githubusercontent.com/u/52085669?v=4", + isAsset: false, + name: "Niket Jain", + email: "niket.jain@mutualmobile.com", + ), + ), + ), + ], + ), + ), + ); + } + + Future _playAnimationForward() async { + try { + await _sizeController.forward().orCancel; + await _rotateTheCubeCounterClockWise(); + await _playAnimationReverse(); + } on TickerCanceled { + // the animation got canceled, probably because we were disposed + } + } + + Future _playAnimationReverse() async { + try { + await _sizeController.reverse().orCancel; + await _rotateTheCubeClockWise(); + await _playAnimationForward(); + } on TickerCanceled { + // the animation got canceled, probably because we were disposed + } + } + + Future _rotateTheCubeCounterClockWise() async { + _rotationAnimation = Tween( + begin: _rotationAnimation.value, + end: _rotationAnimation.value - 2 * pi, + ).animate( + CurvedAnimation( + parent: _rotationController, + curve: Curves.bounceOut, + ), + ); + _rotationController + ..reset() + ..forward(); + } + + Future _rotateTheCubeClockWise() async { + _rotationAnimation = Tween( + begin: _rotationAnimation.value, + end: _rotationAnimation.value + 2 * pi, + ).animate( + CurvedAnimation( + parent: _rotationController, + curve: Curves.bounceOut, + ), + ); + _rotationController + ..reset() + ..forward(); + } + + void _runDraggedReleasedAnimation(Offset pixelsPerSecond, Size size) { + _dragAnimation = _dragAnimateController.drive( + AlignmentTween( + begin: _newOffSetAlignment, + end: Alignment.center, + ), + ); + // Calculate the velocity relative to the unit interval, [0,1], + // used by the animation controller. + final unitsPerSecondX = pixelsPerSecond.dx / size.width; + final unitsPerSecondY = pixelsPerSecond.dy / size.height; + final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY); + final unitVelocity = unitsPerSecond.distance; + + // Play with Damping, Stiffness and Mass value + // To produce your preferred spring simulation + // Kept it very low to produce large spring animation + const spring = SpringDescription( + mass: 1, + stiffness: 50, + damping: 1.00, + ); + + final simulation = SpringSimulation(spring, 0, 1, -unitVelocity); + + _dragAnimateController.animateWith(simulation); + } +} diff --git a/lib/main.dart b/lib/main.dart index 7ab2683..16a256f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'animations/circle_square/circle_square.dart'; import 'animations/animated_container/animated_container.dart'; +import 'animations/crazy_squares/square_container_animation.dart'; import 'animations/day_night.dart'; import 'animations/rainbow_loader/rainbow_loader.dart'; import 'animations/fi_splash/fi_splash.dart'; @@ -50,6 +51,7 @@ class _AnimationsCarouselState extends State { const AnimatedContainerScreen(), const FiSplashScreen(), const DayNightAnimation(), + const SquareContainerAnimation() ]; @override diff --git a/pubspec.lock b/pubspec.lock index 7d046c3..e24e408 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.17.1" convert: dependency: transitive description: @@ -147,10 +147,18 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.18.0" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" lints: dependency: transitive description: @@ -163,18 +171,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" meta: dependency: transitive description: @@ -240,10 +248,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: @@ -288,10 +296,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.5.1" typed_data: dependency: transitive description: @@ -312,10 +320,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.3.0" watcher: dependency: transitive description: @@ -324,14 +332,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" webdriver: dependency: transitive description: @@ -349,4 +349,4 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.0.1 <4.0.0"