Advanced Git, Second Edition

Git is key to great version control and collaboration on software projects.
Stop struggling with Git and spend more time on the stuff that matters!

Home Flutter Tutorials

Adding Micro-Interactions With AnimatedSwitcher

Learn how to add micro-interactions to your Flutter app using AnimatedSwitcher.

5/5 1 Rating

Version

  • Dart 2.13, Flutter 2.0, VS Code

Micro-interactions and animations are key factors in people choosing and continuing to use apps. Thus, adding micro-interactions is important for the success of your app. It hasn’t always been easy, but now with AnimatedSwitcher, you can add a lot of interactivity to your app without investing too much effort.

In this tutorial you’ll learn about:

  • AnimatedSwitcher, how it works and when to use it.
  • Using Key to differentiate widgets.
  • Customizing how AnimatedSwitcher builds its layout.
  • Building custom transitions between widgets with AnimatedSwitcher.
  • Changing the curves in AnimatedSwitcher to provide natural interaction in your app.
Note: This tutorial assumes that you have basic knowledge of widgets and animations. To learn more about the basics, check out Getting Started with Flutter and Implicit Animations in Flutter: Getting Started.

Getting Started

You’ll work on Quotine, an app that inspires people in their daily routine with quotes. Download the starter project by clicking Download Materials at the top or bottom of the tutorial. Then, open the starter project in VS Code 1.57 or later. You can also use Android Studio, but you’ll have to adapt the instructions below as needed.

Use a recent version of Flutter, 2.0 or above. VS Code should show a notification prompting you to click into it to get the dependencies.

If VS Code doesn’t get the dependencies automatically, then download them manually by opening pubspec.yaml and clicking the get package icon on the top-right corner or by running the command flutter pub get from the terminal.

Here’s a quick rundown of how the project is set up:

  • main.dart: Standard main file required for Flutter projects.
  • data.dart: Contains the quotes; think of it as the app’s database for this tutorial’s purpose.
  • domain.dart: Contains the business logic, the Quote model class.
  • app: A folder with the QuotineApp widget, which is the core of Quotine.
  • presentation: Contains two folders: pages, containing all Quotine’s pages, and widgets, containing all the reusable widgets.

Build and run the starter project using the emulator of your preference or even your mobile device. At this point, you’ll see the following:

Quotine cycling through several quotes

As you can see, if the quote displayed isn’t to your liking, you can always skip to the next quote. However, if you look closely, switching between quotes doesn’t feel completely natural. Instead, you could say it’s a bit janky. You’ll fix this by adding your own micro-interactions with AnimatedSwitcher.

Animating With the Right Tools

Before you start coding, it’s important to understand the different animation options provided by the framework.

Flutter has a robust animation library, and inside you can generally classify animations into two groups: implicit animations and explicit animations.

What does that mean? Well, think of implicit animations as already built-in widgets for different needs. For example, AnimatedOpacity animates opacity values or AnimatedList helps animate item addition or removal. In fact, you have a whole list of implicitly animated widgets to choose from.

Explicit animations provide a way for you to control, build and customize the way animations work — for example, coordinating between different animations, repeating your animation in a loop or providing discontinuous effects. As always, Flutter helps you tremendously by adding built-in effects like FadeTransition or SlideTransition, which you’ll use later in this tutorial.

Within the animation library, Flutter also provides a way to handle the duration of your animations. This is key to improving usability: If an animation is too fast, it might be hard to see or dizzying; on the other hand, if it’s too slow, it becomes intrusive and feels like a delay to the user. How to handle duration varies between implicit animations and explicit animations, but Flutter does a good job of making it feel natural for you as a developer.

AnimatedSwitcher can be classified as an implicit animation because it provides a built-in way to transition between two widgets. For example, you can use it to make Text fade in and out when the content changes. It’s highly customizable, so you can play around with different transitions, durations and ways to build the widgets when doing the transition. Here’s an animation you could easily make with AnimatedSwitcher:

AnimatedSwitcher transitioning from a blue square to a green circle

In Quotine’s case, you’ll use AnimatedSwitcher to enhance the user experience and add some awesome micro-interactions. From a technical perspective, you’ll combine a little bit of both implicit animations and explicit animations.

Adding Your First AnimatedSwitcher

Start by opening lib/presentation/pages/home_page.dart. Now, change the child property under // TODO: Replace QuoteDisplay with an AnimatedSwitcher to the following code:

// 1.
child: AnimatedSwitcher(
  // 2.
  duration: const Duration(milliseconds: 500),
  child: QuoteDisplay(
    // TODO: Fix animation bug
    quote: quotes[_currentQuote],
  ),
),

Here’s what you just did:

  1. You wrapped QuoteDisplay with AnimatedSwitcher. This allows you to transition between quotes without worrying too much about how the app handles the animation — yay, Flutter!
  2. Then, you’re setting duration to 500 milliseconds. This makes the transition between widgets slower. It will take 500 milliseconds for the old widget to fade out and will also take the same amount of time for the new widget to fade in.

If you later need to customize the duration of the animation for the widget fading out, you can do it by setting reverseDuration on your AnimatedSwitcher. In this particular case, it’s not necessary.

Build and run. You’ll see something like this:

Quotine cycling through several quotes

Now, don’t get scared — the changes you made are still there! AnimatedSwitcher is there, but a small bug with the code you just wrote is affecting your micro-interaction. Don’t worry, you’ll fix this next.

Understanding the Key Problem

So, what’s happening right now? Why isn’t AnimatedSwitcher working? Actually, it is working, just not in the way you’d expect. Here’s an explanation on the underlying issue you need to fix:

  1. First, your app renders with _currentQuote with value 0. This means QuoteDisplay is being painted with the information of the first quote.
  2. Then, you tap Skip. This changes _currentQuote, incrementing it by one inside setState, triggering a re-render of HomePage.
  3. When rendering again, AnimatedSwitcher tries to figure out if the property child changed, and… it hasn’t. Well, kind of.

The old widget (from Step 1) and the new widget are both of type QuoteDisplay and both have the same key, which is null in this particular case. Because of this, AnimatedSwitcher can’t determine if the child has changed, so it doesn’t do a transition between them. In fact, the framework thinks that they’re the same widget.

So, how can you fix this? It’s actually really simple. Adding a key property to QuoteDisplay helps AnimatedSwitcher determine if something changed. Replace // TODO: Fix animation bug with the following code as a property of QuoteDisplay:

key: ValueKey(quotes[_currentQuote].id),

Build and run. Here’s what it’ll look like now:

Quotine fading from one quote into the next

Great job! Quotine is looking awesome right now.

Adding a Second AnimatedSwitcher

Press Skip a few times. You’ll notice the transition of the text looks great, but the images displayed in the background are just jumping into existence. It would be nice if there was a way to animate them too… Oh, wait, there is — another AnimatedSwitcher!

Open lib/presentation/widgets/image_background.dart. Then, replace child under // TODO: Replace Image with an AnimatedSwitcher with the following code:

child: AnimatedSwitcher(
  duration: const Duration(milliseconds: 500),
  child: Image.network(
    imageUrl,
    key: ValueKey(imageUrl),
    fit: BoxFit.cover,
  ),
),

You just added animation to the image widget the same way you did QuoteDisplay earlier, nothing new.

Look at the result. Build and run, if you haven’t already:

Quotine fading from one quote into the next but backgrounds are too small

Wait, what just happened? The animation is running fine but the Image widget is no longer extending to the borders. Fear not, you shall fix this soon. :]

Adjusting the Background

OK, you just added a second AnimatedSwitcher, but now the background image isn’t extending properly. So, what happened to the widgets? Well, it turns out that the way AnimatedSwitcher builds the layout is affecting child, which in this case is the background image. Below is the default implementation that AnimatedSwitcher uses:

static Widget defaultLayoutBuilder(Widget? currentChild, List previousChildren) {
 return Stack(
    children: [
      ...previousChildren,
      if (currentChild != null) currentChild,
    ],
    alignment: Alignment.center,
  );
}

As you can see, AnimatedSwitcher uses Stack to show the widgets that transition from one to another, placing currentChild — if there is one — on the top, visible always.

So, what’s happening right now on Quotine? Well, by default Stack uses StackFit.loose as the value for fit. This affects the Image widget you use for the background. To fix that, you’ll use a property from AnimatedSwitcher called layoutBuilder.

Add the following code to the background image’s AnimatedSwitcher:

layoutBuilder: (currentChild, previousChildren) {
 return Stack(
    children: [
      ...previousChildren,
      if (currentChild != null) currentChild,
    ],
    fit: StackFit.expand,
  );
},

With this custom implementation of layoutBuilder, you’re basically recreating the effect provided by defaultLayoutBuilder with a couple small changes:

  1. First, you’re using fit: StackFit.expand to force Stack‘s children to expand as much as they can, which in this case is the whole screen.
  2. Then, since the children are expanding, alignment is no longer necessary. This makes the widgets expand to the boundaries of AnimatedSwitcher, and thus, solves the bug you had with the background.

Build and run with the changes. It’ll look like this:

Quotine quotes and backgrounds smoothly fading from one into another

Discovering Transitions

Now, what if you wanted to customize not only the layout of AnimatedSwitcher but also the transition between the widgets? Well, there is a way: by using transitionBuilder.

Look at the default implementation AnimatedSwitcher uses for transitionBuilder:

static Widget defaultTransitionBuilder(Widget child, Animation animation) {
  return FadeTransition(
    opacity: animation,
    child: child,
  );
}

By default, AnimatedSwitcher uses the built-in explicit animation FadeTransition, so you can simply change it to use ScaleTransition instead. You could even make custom Tween animations with the animation parameter provided. Tween is a linear interpolation useful for interpolating across a range. How about you give that last idea a try. Ready?

Add transitionBuilder to AnimatedSwitcher with the following code:

transitionBuilder: (child, animation) {
  // 1.
  final offsetAnimation = Tween(
    begin: const Offset(1.0, 0.0),
    end: const Offset(0.0, 0.0),
  ).animate(animation);
  // 3.
  return ClipRect(
    // 2.
    child: SlideTransition(
      position: offsetAnimation,
      child: child,
    ),
  );
},

Here’s an explanation of what the code you just added means:

  1. You added a Tween animation to handle the explicit SlideTransition that you’ll use for changing between images. begin sets the initial position of the widget and end sets the final position of the widget. Then, you called animate passing animation as a parameter. This gets your Tween ready for use.
  2. Then, you wrapped child to be rendered with SlideTransition and set its position explicitly. This helps SlideTransition figure out where to paint your widgets.
  3. Finally, you also wrapped SlideTransition with a ClipRect widget. This is so the child isn’t shown when it’s out of frame.

It’s important to remember that the transition is used both with the widget entering and the widget exiting, by running the animation in reverse.

Build and run your app to see the results. Here’s how it’ll look:

Quotine quotes and backgrounds transitioning via animation from one into another

Learning About Curves

You’re almost done adding micro-interactions to Quotine, and it’s already looking like a completely different app than when you started — great work! But there’s one last thing you’ll learn about before releasing the new version of the app, and that’s Curves.

When you’re adding animations to an app, you have to think of real-world behaviors for the different objects you’re animating. If you’re trying to add a ball bouncing up and down, you have to think of the effect gravity has on it. For example, if the ball is heavy, each bounce will be significantly smaller, and you might also see that with each bounce the ball gets slower. Here’s a regular bouncing animation:

Gray square bouncing

In the example above, the effect of going up and down is an animation effect. You can apply a number of effects to animations to make them visually interesting. You usually apply them using curves to influence how the animation behaves. The basic bouncing animation above uses an easeOutExpo curve when the ball is going upward and easeInExpo when the ball is going downward.

You can see all the available Curve implementations in the Curves API documentation. You have at least 38 different options to choose from, so choose wisely!

Customizing With Curves

OK, you know about Curves. Cool. But how do you implement them with AnimatedSwitcher, you ask?

AnimatedSwitcher has two properties for this: switchInCurve, which is the animation curve to use when transitioning in a new widget; and switchOutCurve, which is the animation curve to use when transitioning a previous widget out. It’s very similar to the bouncing ball example you saw in the previous section.

Apply the knowledge you just gathered about curves to customize the way the transitions behave.

Next, add switchInCurve for AnimatedSwitcher in lib/presentation/widgets/image_background.dart, which looks like this:

switchInCurve: Curves.easeOutExpo,

Then, add switchOutCurve for AnimatedSwitcher in lib/presentation/pages/home_page.dart like this:

switchOutCurve: Curves.easeInExpo,

Here’s what the final result will look like:

Quotine quotes and backgrounds transitioning via animation from one into another

Awesome job! You just added micro-interactions to Quotine — you’re definitely a rock star!

Where to Go From Here?

You can download the completed project files by clicking Download Materials at the top or bottom of the tutorial.

Still having doubts about using micro-interactions in your app? Check out Micro-Interactions: a designer’s superpower to learn more about how they can drastically improve your user experience.

You can deeply customize the interactivity by creating your own Tween animations and combining them inside transitionBuilder. Learn more about Tween by checking out Flutter’s Animations tutorial on essential concepts and classes for animations.

If you want to learn about mixing up different animations within your app, then take on the challenge of completing the Implicit Flutter Animations video tutorial here at raywenderlich.com.

We hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!

Average Rating

5/5

Add a rating for this content

1 rating

More like this

Contributors

Comments