Create Simple Animations In Flutter

You can create animations with the TweenAnimationBuilder widget in Flutter. A great animation can differentiate among web pages. If you want your page to stand out, you have to make everything in your arsenal to make it happen. Animations are little extras, that you want to know from up to bottom and want to use them with ease.

It should not be a drag to create your own animations. Today I am going to show you the easiest way of making animations in Flutter.

Why develop animations on your own?

Nowadays you cannot prevail on the internet without doing something more than your competitors. It can be anything. A great product, a revolutionary business model, or event the animations if they are stunning. But why develop animations and not search for them on the internet. Two reasons.

The animation that your heart desires may not be found on the internet, or it is, but there are something you would like some other way.

By creating your own animations, you are slowly developing yourself technically, you will have practise, you will be able to create any animations you want. It is highly customizable.

If there come a time when you, or your customer want a difficult animation, you can create it and satisfy your or the customer’s wish. And trust me, customer’s will not look at the backend code, or the frontend code. They are not interested in how the page works (most of them are not), but they are very interested in the look and feel of the final product. If the customer likes what he/she sees, he will be a reoccuring client of yours.

When to use animations?

Well everything you know about UI/UX, you can apply to making animations. Let your imagination fly. But it can be broken down to these fundamental points:

Use animations where and when they are necessary. Everyone remembers those kitschy mouse animations from the early 2000s, when you moved the mouse the some glistering partical would come out from the cursor. Yikes! Back then you could find those in nearly every website, and they were all terrible. Fight me on this!

https://blog.humphd.org/the-technology-of-nostalgia/

The theme of the animation should match with the theme of the website. Is it for kids website? Put a lot in it, play with it, kids will love them. Is it for some official website? Maybe tune it back a bit. Noone wants to see santa waving when doing their tax. Okay, you got me, a waving santa could fit in everywhere.

How animations work?

The animations I am going to show you are all going to work by the same principle. We are going to have an interval, where we are going to be incrementing and decrementing a number. That number could be then used for whatever you’d like.

During the animation, we are going to rebuild the component as many times as necessary, until the number gets from the one end of the interval, to the other. We can make the animation faster or slower, by changing its duration. I am always using milliseconds, because it gives me a good precision of controlling the speed of the animation.

There are other methods of animation, but in my opinion this is the easiest way, that not require any additional mixins for our class.

Why I don’t use Ticker

Creating an animation with SingleTickerProviderStateMixin is just a bit harder than creating an animation without them. It has more boilerplate code, than the code I am going to show you. For you it could be a minor difference, but from a programmer’s standpoint if you can make your code smaller and easier to read, then you should do it that way.

Animating in Flutter

In my opinion currently the best and easiest way of animating in Flutter is the TweenAnimationBuilder widget. It works as described, it moves a number beTWEEN the start and end point of the given interval.
The parameters I am going to be using are:

  • Tween, which is the interval description object, which has begin and end properties.
  • Duration for the speed of the animation.
  • Curve, which describes the movement of the animation. The Curves class has several predefined Curve objects, that you can use, from linear movements to bouncy ones.
  • The onEnd callback function. It is a great method to turn around your animation repeat it, or just do something when the animation completes.
  • And lastly the builder method, which gives back the current number between the interval. Within that method, we are going to be building the animation.

Examples

We are going to make 5 examples:

  • Menu animation
  • Multiline loading animation
  • Single line loading animation
  • Warning sign animation
  • Scrollbar animation

Every animation has common components like the AnimationState enum, which will tell us what state the animation is.

Menu animation

Let’s start with the easiest, just to warm up. Our task is to change the height of the menu, when the cursor is hovering on top of the header. So we need the MouseRegion widget, so we can keep track of the animation state.

Widget _getMouseRegion(Widget child) {
  return MouseRegion(
    onEnter: (s) {
      setState(() {
        animationState = AnimationState.OUT;
      });
    },
    onExit: (s) {
      setState(() {
        animationState = AnimationState.IN;
      });
    },
    child: child,
  );
}

The build method

Widget build(BuildContext context) {
  Tween tween;

  if (animationState == AnimationState.OUT) {
    tween = Tween<double>(begin: MIN_HEIGHT, end: MAX_HEIGHT);
  } else {
    tween = Tween<double>(begin: MAX_HEIGHT, end: MIN_HEIGHT);
  }

  return TweenAnimationBuilder<double>(
    tween: tween,
    duration: Duration(milliseconds: 200),
    curve: Curves.bounceIn,
    builder: (BuildContext cont, double height, Widget w) {
      return _buildBody(height);
    },
  );
}

And the body

Widget _buildBody(double height) {
  return _getMouseRegion(
    Center(
      child: Container(
        height: height,
        width: double.infinity,
        color: Colors.blue,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            Text("Home", style: TextStyle(color: Colors.white, fontSize: 22)),
            SizedBox(width: 20),
            Text("Login", style: TextStyle(color: Colors.white, fontSize: 22)),
            SizedBox(width: 20),
            Text("Contacts", style: TextStyle(color: Colors.white, fontSize: 22)),
            SizedBox(width: 20),
          ],
        ),
      ),
    ),
  );
}

The next will be slightly harder, but still a simple animation.

Multiline loading animation

Because when would you want an animation the most? Of course when there is nothing happening on the screen and you want to show the user that, something IS happening behind the curtains, so just hold your horses and don’t click away.

This animation, unlike the previous, will be repeated infinitely, so we are using he onEnd callback to change the state when the animation ended.

Widget build(BuildContext context) {
  Tween tween;
  switch (animationState) {
    case AnimationState.IN:
      tween = Tween<double>(begin: MAX_WIDTH, end: MIN_WIDTH);
      break;
    case AnimationState.OUT:
      tween = Tween<double>(begin: MIN_WIDTH, end: MAX_WIDTH);
      break;
  }

  return TweenAnimationBuilder<double>(
    tween: tween,
    duration: Duration(milliseconds: 550),
    curve: Curves.linear,
    onEnd: () {
      setState(() {
        if (animationState == AnimationState.IN) {
          animationState = AnimationState.OUT;
        } else {
          animationState = AnimationState.IN;
        }
      });
    },
    builder: (BuildContext cont, double width, Widget w) {
      return _buildBody(width);
    },
  );
}

The line builder is has a bit of math in it, because there are 2 groups of lines, group 0 is the first and third, groupt 1 is the second and fourth line. That is important, because we have to differentiate among the lines. When group 0 has full height, group 1 should have the minimum height, and vica versa.

Widget _getloadingLine(double width, int groupId) {
  double animatedWidth = width;
  if (groupId == 1) {
    animatedWidth = MIN_WIDTH + (MAX_WIDTH - width);
  }

  return Container(
    height: 3,
    width: animatedWidth,
    color: Colors.blue,
  );
}

The body is farily simple.

Widget _buildBody(double width) {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      _getloadingLine(width, 0),
      SizedBox(height: 8),
      _getloadingLine(width, 1),
      SizedBox(height: 8),
      _getloadingLine(width, 0),
      SizedBox(height: 8),
      _getloadingLine(width, 1),
    ],
  );
}

Single line loading animation

So I came up with another simple loading indicator, which is just a simple line, that has 2 colors, when one is gaining, the other is disappearing. Simple enough, so how is it?

The build method is the same as before, with one minor change. Up to this point I used linear curves for the animation movement. Now I am going to use another, which in this example, looks better. That is the decelerate curve, which slowly decelerates when reaching the endpoint.

curve: Curves.decelerate,

Then we have to build 2 lines, in a row, with different colors and widths.

Widget _buildBody(double width) {
  return Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      _getloadingLine(width, Colors.blue[300]),
      _getloadingLine(MAX_WIDTH - width, Colors.greenAccent),
    ],
  );
}

Widget _getloadingLine(double width, Color color) {
  double animatedWidth = width;

  return Container(
    height: 5,
    width: animatedWidth,
    color: color,
  );
}

Warning sign animation

Once again the build method stays the same, but this time, we are going to return the widget straight in the builder method.

builder: (BuildContext cont, double colorNum, Widget w) {
  return Center(child: _getCube(colorNum.floor()));
},

We are going to finish off with a WARNING text and the color attribute, in the following way.

Widget _getCube(int colorNum) {
  return Container(
    decoration: BoxDecoration(
      borderRadius: BorderRadius.all(Radius.circular(30)),
      color: Color.fromARGB(255230, colorNum, colorNum),
    ),
    height: 42,
    width: 130,
    child: Center(
      child: Text(
        "WARNING",
        style: TextStyle(color: Colors.white, fontSize: 22),
      ),
    ),
  );
}

Srollbar animation

In my previous post , I made a generic feel scrollbar for Flutter web. Now I am going to extend the functionality of that scrollbar, with the Menu hide/show animation. I have created a widget, that changes the state of the animation when the cursor interacts with the scrollbar. This widget looks similar to the Menu example state handler.

Widget _getMouseRegion(Widget child) {
  return MouseRegion(
    onEnter: (s) {
      setState(() {
        thumbAnimation = ThumbAnimation.OUT;
      });
    },
    onExit: (s) {
      setState(() {
        thumbAnimation = ThumbAnimation.IN;
      });
    },
    child: child,
  );
}

I ended up using this method on the scrollbar background and on the scrollthumb itself.

return Stack(
  fit: StackFit.loose,
  alignment: Alignment.topRight,
  children: [
    widget.child,
    _getMouseRegion(_getScrollbarBackground(width)),
    _getScrollThumb(width),
  ],
);

Then adjusted the width in both of the functions.

Conclusion

So now you know how to create simple animations in Flutter. My advice to you is to come up with your own ideas regarding the animations, so the internet will be full of unique and interesting animations. Stand out with your creativity.

The whole code is available on my gitlab account.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s