Fun Animations in Flutter part -2

Ankit Gupta
6 min readSep 5, 2020

--

Hi and welcome back once again in this continued part of fun animations in flutter you can refresh your previous knowledge on flutter animation or if you missed it get through it

So as I promised to have some fun with animations in a flutter so I have decided to look back at the elementary level of our schooling where we actually have been firstly creative in the early stage of our life yes the DRAWING class. Today we will consider this as an early class in the School Of Flutter Animations.

Before we get started let's look at what we are going to build.

Yes, the scenery of the windmill with some Clouds, Birds, and bees animating.

Stay cool everything is gonna be easy.

First, let's see our code,

Don't worry we are gonna see them in understandable snippets.

Since we are looking for animation in a flutter I assume you are familiar with the code above the WindMill widget.

class WindMill extends StatefulWidget {
@override
_WindMillState createState() => _WindMillState();
}
class _WindMillState extends State<WindMill> with TickerProviderStateMixin {
AnimationController birdAndCloudController;
AnimationController beeController;
AnimationController windmillController;

Animation<double> flyAnimation;
Animation<double> moveLeftToRightAnimation;
Animation<double> beeAnimation;
Animation<double> windmillAnimation;

Since here we are using multiple animations we are going to extend our widgets state class with TickerProviderStateMixin, For the case of a single animation, we would prefer SingleTickerProviderStateMixin.

here we are using three different controllers and their names clear the purpose they are used for. Further, we have four different values of class Animation holding the values of double this is simply the tween range for which they have to play the animation.

Now let's look at how they are set up to perform an animation.

initState is the place where logically we should set up our initialization logic and listeners in a StatefulWidget

 

// Animation setup for WindMill

windmillController = AnimationController(vsync: this, duration: Duration(seconds: 20));

windmillAnimation = Tween<double>(begin: 0, end: 2 * math.pi).animate(windmillController) ..addStatusListener((status) {
if (status == AnimationStatus.completed){ windmillController.reverse();
} else if (status == AnimationStatus.dismissed) { windmillController.forward();
}
});
windmillController.forward();

first let's look at windmillController and windmillAnimation

we have initialized the controller with the two important parameters of vsync and duration as discussed in the previous article, now let's talk about the initialization of windmillAnimation.

Since this is the windmill we know this can rotate 360 degrees in both clockwise and anticlockwise direction so we initialize this simply with the TWEEN<double> from the beginning value of 0 and ending value to 2 * pi (360 degrees) in radians and provide it with the controller to animate, depending upon the status of animation we rotate this forward and reverse, Note the last line where we declare the controller to move forward this is what kicks our animation to start and rest is the work of our

..addStatusListener()

to perform clockwise and anticlockwise animation.

//  Animation Setup for beeAnimation  

beeController = AnimationController(vsync: this, duration: Duration(seconds: 3));
beeAnimation = Tween<double>(begin: 0, end: 100) .chain(CurveTween(curve: Curves.bounceInOut)) .animate(beeController)..addStatusListener((status) {
if (status == AnimationStatus.completed) { beeController.reverse();
} else if (status == AnimationStatus.dismissed) { beeController.forward();
}
});
beeController.forward();

here in bee animation same logic is applied as above with only as small change of introducing the curve

.chain(CurveTween(curve: Curves.bounceInOut))

by default the animations in flutter are linear if no curve is provided means it is a linear animation else for some different type you can check

Coming to Cloud and bird animation

// Cloud and Bird animation set up   

birdAndCloudController = AnimationController( duration: Duration(seconds: 5), vsync: this,);

flyAnimation = Tween<double>( begin: 0, end: -100,) .chain(CurveTween(curve: Curves.easeInOutBack)) .animate(birdAndCloudController);

moveLeftToRightAnimation = Tween<double>( begin: 0,end: 350, ).chain(CurveTween(curve: Curves.easeIn)).animate(birdAndCloudController); birdAndCloudController.repeat();

here we are using two animation class one for bird and cloud and other for moving left to right we did this here just to add on some detail to the animation of bird to see this as a bird taking a flight from bottom to top in the sky here for the fly animation our starting point is 0 while our ending point is -100 this is so our bird fly up in sky for positive value it will take a flight down. Other animation is only to move from left to right, and we say our controller to repeat the animation continuously.

And now the important thing dispose of all your controllers.

@override  
void dispose() {
windmillController.dispose();
birdAndCloudController.dispose();
beeController.dispose();
super.dispose();
}

dispose controller in order to avoid memory leaks.

Now lets come to build method

Here we have used Stack so to create different details by overlapping the widgets on top of one another the background is created by Column with two Containers divided into Sky and Ground.

Lets keep this short and focus on only important Ones.

First is BirdBuilder

class BirdBuilder extends StatelessWidget {
const BirdBuilder({ Key key, @required this.control, @required this.linear, @required this.fly, }) : super(key: key);
final AnimationController control;
final Animation<double> linear;
final Animation<double> fly;
@override Widget build(BuildContext context) {
return AnimatedBuilder(
animation: control,
builder: (_, child) => Stack(children: <Widget>[ Positioned(top: 100,
left: linear.value,
child: Transform.translate( offset: Offset(0, fly.value), child: Image.asset('assets/bird.png', height: 35, color: Colors.white,
),
),
),
]));
}}

here we pass three required parameter namely - control for birdAndCloudController, linear for leftToRight and fly for flyAnimation

Our AnimatedBuilder will take rebulids itself with ever ticker tick from TickerProviderStateMixin probably 60 fps acc to flutter docs with every new ticks will generate a new value for each animations and our Stack will rebuild with new left value and Transfrom.translate will rebuild with new Offset value and will result in desired animation.

WindmillBuilder

class WindmillBuilder extends StatelessWidget {const WindmillBuilder({ Key key, @required this.animation, }) : super(key: key);   final Animation<double> animation;

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: Container(
height: 250,
width: 250,
child: Stack(
alignment: Alignment.center,
children: [
Container(
height: double.infinity,
width: 40,
decoration: BoxDecoration(
color: Colors.pink[300],
borderRadius: BorderRadius.circular(20)),
),
RotatedBox(
quarterTurns: 1,
child: Container(
padding: EdgeInsets.all(5),
height: double.infinity,
width: 40,
child: CircleAvatar(
backgroundColor: Colors.orange,
),
decoration: BoxDecoration(
color: Colors.pink[300],
borderRadius: BorderRadius.circular(20)),
),
)
],
),
),
builder: (BuildContext context, Widget child) {
return RotationTransition(
turns: animation,
child: child,
);
}); }}

Here we need to pass a required parameter of windmillAnimation.

Remember about the animation optimization we achived in our previous tutroial by cache our widget subtree we finally got the chance to apply this over here were we can see in animated builder we have assigned child attribute with some nested widgets this is what get cached and our builder functions only rebuilds RotationTransition and not its child which is our nested widgets. Here we have use a RotatedBox and made its child to turn one quarter this is because it dosen’t overlap the container ( blades of windmill ) above it which will cause it to look one even when there are two.

CloudBuilder

class CloudBuilder extends StatelessWidget {  
const CloudBuilder({ Key key, @required this.control, @required this.leftToRight, }) : super(key: key);
final AnimationController control;
final Animation<double> leftToRight;
@override Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 50),
height: 100,
width: double.infinity,
child: AnimatedBuilder(
animation: control,
builder: (_, child) => Stack(
children: [
Positioned(
left: leftToRight.value,
child: Transform.translate(
offset: Offset(leftToRight.value, 0),
child: Icon(
Icons.cloud,
size: 40,
color: Colors.white,
)),
),
],
),
),
);
}}

Over here our cloud builder takes two arugments control for birdAndCloudController second animation for leftToRightanimation.

and the builder rebuilds with new Positioned left and a new offset value for Transform.translate.

BeeBuilder

class BeeBuilder extends StatelessWidget {  const BeeBuilder({ Key key, @required this.beeController,    @required this.beeAnimation,  }) : super(key: key);
final AnimationController beeController;
final Animation<double> beeAnimation;
@override Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 20),
height: 150,
width: double.infinity,
child: AnimatedBuilder(
animation: beeController,
builder: (_, child) => Stack(
children: <Widget>[
Positioned(
bottom: 100,
left: beeAnimation.value,
child: Transform.translate(
offset: Offset(0, beeAnimation.value), child: Image.asset( 'assets/bee.png',
height: 30,
),
),
),
])),
);
}}

Here One again the logic gets repeated.

So finally we have completed our windmill.

Our next Drawing class, we will learn to hold the paintbrush and use our canvas to draw things.

--

--