Flutter

Animations in Flutter – Scale and Size Transitions

By 7 October 2020 No Comments

Animations in Flutter – Scale and Size Transitions

Animations in Flutter

Sharing is caring!

While writing this article, I’m assuming you’re already familiar with the basic understanding of Animation. You can also research the term on Google and will find a lot of resources which explains the same. You can also visit the documentation of Animations in Flutter where you will find it as a vast topic. Please feel free to explore it and you will end up knowing how Animation can bring a nicer UI and improved UX to your Flutter application.

I’ve used the same documentation and used it here to give you a better overview on Animations in Flutter

from flutter.dev

Let me dive you into my experience with Animations for one of my Flutter projects. First we will see the basics about how Animation works in Flutter, later we will see kinds of Animated Scale and Size transitions.

AnimationController

Just as the phrase says, it controls the Animation you want to perform like:

  • Duration of the Animation and so the speed.
  • Play Animation forward or backwards
  • Stop animation, and so on.

For example, we can specify our Animation cController like:

_controller = AnimationController(duration: Duration(milliseconds: 1500), vsync: this);

where, vsync is of type TickerProvider.

Tickers

From the documentation in android studio:

Tickers can be used by any object that wants to be notified whenever a frame triggers. They are most commonly and indirectly used via an AnimationController. AnimationController need a TickerProvider to obtain their Ticker. If you are creating an AnimationController from a State, then you can use the TickerProviderStateMixin and SingleTickerProviderStateMixin classes to obtain a suitable TickerProvider.

Animation<T>

This is the type of Animation we wish to perform with a value of type ‘T’. At the time of writing this article, I found there are 4 kind of Animations we can use:

  1. ProxyAnimation
  2. ReverseAnimation
  3. CurvedAnimation
  4. TrainHoppingAnimation

We will be using CurvedAnimation for Scale and Size transitions. I will be demonstrating the same here, as its useful for applying a non-linear curve to an Animation object. For example:

_animation = CurvedAnimation(parent: _controller, curve: Curves.bounceOut);

Let’s dive into Transitions.

ScaleTransition

This is nothing but the widget which will animate itself while being drawn. It requires the scale parameter to be passed while other two parameters – alignment and child are optional. For example:

ScaleTransition(
      scale: _animation,
      alignment: Alignment.center,
      child: Icon(Icons.image, size:50.0)
    )

If you want to use this Scale Animation on ten difference places in flutter app, it’s not suggested to write the entire code including controller, animation and transition everywhere we need. It is always better to wrap the code in a separate stateful widget and use based on requirement. Let us move forward.

Create scale_animation_widget.dart file, and paste the code below in that.

import 'package:flutter/material.dart';

class ScaleAnimationWidget extends StatefulWidget {
  
  final Widget child;
  final Duration duration;
  final Curve curve;
  
  ScaleAnimationWidget({@required this.child, this.duration = const Duration(milliseconds: 1500), this.curve = Curves.bounceOut});

  @override
  _ScaleAnimationWidgetState createState() => _ScaleAnimationWidgetState();
}


class _ScaleAnimationWidgetState extends State<ScaleAnimationWidget> with SingleTickerProviderStateMixin {

  AnimationController _controller;
  Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: widget.duration, vsync: this);
    _animation = CurvedAnimation(parent: _controller, curve: widget.curve);
    _controller.forward();
  }

  @override
  dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ScaleTransition(
        scale: _animation,
        alignment: Alignment.center,
        child: widget.child
    );
  }
}

Usage Example:

ScaleAnimationWidget(child:Image.asset('assets/png/success.png'))

SizeTransition

I will use this widget for expanding and collapsing behaviour. Let’s say there is a button with onPress method. Upon pressing that button the widget must expand if collapsed, or collapse if expanded. We can achieve this expand/collapse behaviour using SizeTransition animation. Unlike ScaleTransition, SizeTransition requires the sizeFactor parameter, while axis, axisAlignment and child parameters are optional. For example:

SizeTransition(
    axisAlignment: 1.0,
    sizeFactor: _animation,
    child: Icon(Icons.image, size:50.0)
);

Let’s wrap our code for SizeTransition as separate widget so as to make it reusable:

Create size_animation_widget.dart file and paste the following code in that.

import 'package:flutter/material.dart';

class SizeAnimationWidget extends StatefulWidget {

  final Widget child;
  final Duration duration;
  final Curve curve;
  final bool expand;

  SizeAnimationWidget({@required this.child, @required this.expand, this.duration = const Duration(milliseconds: 500), this.curve = Curves.easeInOut});

  @override
  _SizeAnimationWidgetState createState() => _SizeAnimationWidgetState();
}

class _SizeAnimationWidgetState extends State<SizeAnimationWidget> with SingleTickerProviderStateMixin {

  AnimationController _controller;
  Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: widget.duration, vsync: this);
    _animation = CurvedAnimation(parent: _controller, curve: widget.curve);
    _runExpandCheck();
  }

  void _runExpandCheck() {
    if(widget.expand) {
      _controller.forward();
    }
    else {
      _controller.reverse();
    }
  }

  @override
  void didUpdateWidget(SizeAnimationWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    _runExpandCheck();
  }

  @override
  dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizeTransition(
        axisAlignment: 1.0,
        sizeFactor: _animation,
        child: widget.child
    );
  }
}

If you have observed the above code, there is _runExpandCheck() method which handles the expanding and collapsing the widget based on the ‘expand’ flag, using forward() and reverse() methods on the animation object. We call this method initially and at a later stage whenever the widget will be updated.

Usage Example:

...
bool _showDetails = false;
...
@override
Widget build(BuildContext context) {
  return Column(
    children: <Widget>[
      IconButton(icon: Icon(_showDetails ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down), 
      onPressed: () => setState((){ _showDetails = !_showDetails;})
      ),
      SizeAnimationWidget(child: Container(color:Colors.green, height:100.0), expand: _showDetails)
    ],
  );
}

Size Transition That’s all about Scale and Size Transition animations for now. Stay tuned tomy blog to know more about animations. You can use the comments column below, for raising your queries if any. Thanks for reading!

Get started with Bloom