First: Intro To Animation Components

The First Component is Animation (obviously) and the Second is AnimationController. Animation object is the object that gets the values that are given and translates those to meaningful animations. AnimationController class is the class to control the animation objects. We are using it to start the animation objects, give them durations and many more.

Nextly,

Tween is a Component which is used to mapping from an input range to output range. i.e, it is used to change to values from an initial point to final point

Here’s the Basic Code:

import 'package:flutter/material.dart';

import 'package:flutter/animation.dart';

import 'package:flutter/services.dart';



void main(){

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);

runApp(Myapp());

}



class Myapp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

title: 'Animation',

debugShowCheckedModeBanner: false,

home: HomePage(),

);

}

}

class HomePage extends StatefulWidget {

@override

_HomePageState createState() => _HomePageState();

}



class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title:Text('Animation')),

body: Container(),

);

}

}

The Above code Generates an app with UI theme as light(SystemUiOverlayStyle.light) and Creates a Scaffold Widget with an Appbar Titled as ‘Animation’ and a Container as a child.

You may wonder What is SingleTickerProviderStateMixin.

These Give access Ticker objects, The ticker objects listen to refresh rate and return numbers Which are Then provided to AnimationController for Further Animation

Ok, Now Create a Card with an elevation of value 30.0 inside a Container of a Center Widget

code:

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title:Text('Animation')),

body: Center(

child:Container(

width: MediaQuery.of(context).size.width-100,

height: 300,

child:Card(

elevation: 30.0,

)

)

),

);

}

We gave the Container width as “MediaQuery.of(context).size.width-100"

The MediaQuery.of(context).size.width returns a value Dynamic Width Based on Your Device and (-100) makes it looks Good we subtract it by 100.

Your Output Should be like this if you Run The app:

Ok, Now let's Create Animation and AnimationController Components in the Main HomePageState

Animation<double> animation;

AnimationController animationController;

In Order to Initialize the Animation, We need to override the initState() method and we need to initialize the AnimationController and Animation and provide Animation Values To it.

...

AnimationController animationController; void initState() {

// TODO: implement initState

super.initState();

animationController =

AnimationController(vsync: this, duration: Duration(milliseconds: 1000));

animation = Tween(begin: 30.0, end: 0.0).animate(animationController)

..addListener(() {

setState(() {});

});

animationController.forward();

} @override

Widget build(BuildContext context) {

...

We provided vsync which prevents offscreen animations from consuming unnecessary resources and setting the duration as 1000millisecs(1 sec)

and we set animation values as Tween Component which begins with values 30.0 and ends with value “0.0” and {.animate(animationController)} and setState is to do change the animation State.

animation.forward() Begins the animation.

To Repeat the Animation Over and Over Again we Need to Listen For animation to Complete and reverse it and again. For that, we need a StatusListener and here it Goes:

...

setState(() {});

}); animation.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController.reverse();

} else if (status == AnimationStatus.dismissed) {

animationController.forward();

}

}); animationController.forward();

...

Now your Output Should Look Like this:

Gooooooood isn’t it!

Now Our {Future} Work is Going to be more Simpler, You may guess it by now!

We just need to add a card inside this card and Stack upon them Like this:

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: Text('Animation')),

body: Center(

child: Container(

width: MediaQuery.of(context).size.width - 100,

height: 300,

child: Card(

elevation: animation.value,

child: Padding(

padding: EdgeInsets.all(30.0),

child: Card(

elevation: animation.value,

child: Padding(

padding: EdgeInsets.all(30.0),

child: Card(

elevation: animation.value,

child: Padding(

padding: EdgeInsets.all(30.0),

child: Card(

elevation: animation.value,

)),

),

),

),

)))),

);

}

We Actually set the Values of Elevation to animation.value and We set the Padding in Order to Look Good.

And Looks like this:

And In Order to Start the Animation only when we Tap on the Center Card we need to add a Button as a child to the topmost card and set it's to onPressed value to start the animation when pressed. and Remove the Animation.forward() on the initState() method.

...

child: Card(

child: MaterialButton(onPressed: () {

animationController.forward();

}),

elevation: animation.value,

)),

...

Finally,

we need to do one more thing, which is to get that Beautiful animation in order to get that we need Couple of more aminationControllers and Animation value in which the AnimationControllers should begin the animation whenever the animation of the before Card finishes its Animation and reverse it as per its Card behind it.

Just Add Couple of more Animation, AnimationControllers with different names.

class _HomePageState extends State<HomePage> with TickerProviderStateMixin { Animation<double> animation;

Animation<double> animation2;

Animation<double> animation3;

Animation<double> animation4;

AnimationController animationController;

AnimationController animationController2;

AnimationController animationController3;

AnimationController animationController4; @override

void initState() {

...

and the logic is Pretty Simple and will be Explained below:

@override

void initState() {

super.initState();



animationController =

AnimationController(vsync: this, duration: Duration(milliseconds: 400));



animationController2 =

AnimationController(vsync: this, duration: Duration(milliseconds: 500));



animationController3 =

AnimationController(vsync: this, duration: Duration(milliseconds: 600));



animationController4 =

AnimationController(vsync: this, duration: Duration(milliseconds: 700));



animation = Tween(begin: 30.0, end: 0.0).animate(animationController)

..addListener(() {

setState(() {});

});



animation2 = Tween(begin: 30.0, end: 0.0).animate(animationController2)

..addListener(() {

setState(() {});

});



animation3 = Tween(begin: 30.0, end: 0.0).animate(animationController3)

..addListener(() {

setState(() {});

});



animation4 = Tween(begin: 30.0, end: 0.0).animate(animationController4)

..addListener(() {

setState(() {});

});



animation.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController2.forward();

}

});



animation2.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController3.forward();

} else if (status == AnimationStatus.dismissed) {

animationController.reverse();

}

});



animation3.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController4.forward();

} else if (status == AnimationStatus.dismissed) {

animationController2.reverse();

}

});



animation4.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController4.reverse();

} else if (status == AnimationStatus.dismissed) {

animationController3.reverse();

}

});

}

Basically, what we have done here is “addStatusListener” to all the Animation Components and if the Last Card Finishes its Animation we want the Respective AnimationController of the previous to Start its Animation!

and Vice Versa!!!

Don’t Forget to Change the Elevation Properties in Each Card to its Respective Animation Components.

And Change the SingleTickerProviderStateMixin to TickerProviderStateMixin In Order to Perform Multiple Animation as mentioned Earlier.

Here’s the Final Code:

import 'package:flutter/material.dart';

import 'package:flutter/animation.dart';

import 'package:flutter/services.dart';



void main() {

SystemChrome.setPreferredOrientations(

[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);



runApp(Myapp());

}



class Myapp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

title: 'Animation',

debugShowCheckedModeBanner: false,

home: HomePage(),

);

}

}



class HomePage extends StatefulWidget {

@override

_HomePageState createState() => _HomePageState();

}



class _HomePageState extends State<HomePage>

with TickerProviderStateMixin {

Animation<double> animation;

Animation<double> animation2;

Animation<double> animation3;

Animation<double> animation4;

AnimationController animationController;

AnimationController animationController2;

AnimationController animationController3;

AnimationController animationController4;



@override

void initState() {

super.initState();



animationController =

AnimationController(vsync: this, duration: Duration(milliseconds: 400));



animationController2 =

AnimationController(vsync: this, duration: Duration(milliseconds: 500));



animationController3 =

AnimationController(vsync: this, duration: Duration(milliseconds: 600));



animationController4 =

AnimationController(vsync: this, duration: Duration(milliseconds: 700));



animation = Tween(begin: 30.0, end: 0.0).animate(animationController)

..addListener(() {

setState(() {});

});



animation2 = Tween(begin: 30.0, end: 0.0).animate(animationController2)

..addListener(() {

setState(() {});

});



animation3 = Tween(begin: 30.0, end: 0.0).animate(animationController3)

..addListener(() {

setState(() {});

});



animation4 = Tween(begin: 30.0, end: 0.0).animate(animationController4)

..addListener(() {

setState(() {});

});



animation.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController2.forward();

}

});



animation2.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController3.forward();

} else if (status == AnimationStatus.dismissed) {

animationController.reverse();

}

});



animation3.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController4.forward();

} else if (status == AnimationStatus.dismissed) {

animationController2.reverse();

}

});



animation4.addStatusListener((status) {

if (status == AnimationStatus.completed) {

animationController4.reverse();

} else if (status == AnimationStatus.dismissed) {

animationController3.reverse();

}

});

}



@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: Text('Animation')),

body: Center(

child: Container(

width: MediaQuery.of(context).size.width - 100,

height: 300,

child: Card(

elevation: animation4.value,

child: Padding(

padding: EdgeInsets.all(30.0),

child: Card(

elevation: animation3.value,

child: Padding(

padding: EdgeInsets.all(30.0),

child: Card(

elevation: animation2.value,

child: Padding(

padding: EdgeInsets.all(30.0),

child: Card(

child: MaterialButton(onPressed: () {

animationController.forward();

}),

elevation: animation.value,

)),

),

),

),

)))),

);

}

}

The Duration of Each Animation Can be Increased and Decreased by Changing the Duration of the AnimationController.

To set the App in Portrait Mode ONLY add these to your main() function. void main(){

SystemChrome.setPreferredOrientations(

[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); runApp(Myapp());

}

And Done!

Glad! You Gave me a couple of Claps!😊😊

Here’s my Github Repo! Hope you give Stars!🌟🌟