How to create unique animations in application using flutter
Apps for any platform are praised when they are intuitive, good-looking, and provide pleasant feedback to user interactions. Animation is one of the ways to do just that.
Flutter, a cross-platform framework, has matured in the past two years to include web and desktop support. It has garnered a reputation that apps developed with it are smooth and good-looking. With its rich animation support, declarative way of writing UI, “Hot Reload,” and other features, it is now a complete cross-platform framework.
If you are starting out with Flutter and want to learn an unconventional way of adding animation, then you are at the right place: we will explore the realm of animation and motion widgets, an implicit way of adding animations.
Flutter is based on the concept of widgets. Each visual component of an app is a widget — think of them as views in Android. Flutter provides animation support using an Animation class, an “AnimationController” object for management, and “Tween” to interpolate the range of data. These three components work together to provide smooth animation. Since this requires manual creation and management of animation, it is known as an explicit way of animating.
Now let me introduce you to animation and motion widgets. Flutter provides numerous widgets which inherently support animation. There’s no need to create an animation object or any controller, as all the animation is handled by this category of widgets. Just choose the appropriate widget for the required animation and pass in the widget’s properties values to animate. This technique is an implicit way of animating.
Some of the animated widgets used in this app are:
· AnimatedOpacity
· AnimatedCrossFade
· AnimatedAlign
· AnimatedPadding
· AnimatedSize
· AnimatedPositioned.
Getting Started
Codes in file-1:
My content in the main.dart file, with the code snippet:
I have created another .dart file inside lib to manage all the classes and I have named it anicon.dart. anicon.dart will contain all the code responsible for all the visual elements (widgets) required for our Quoted app. All the animation is handled in this file. Inside the anicon.dart file I have create the following classes and added the required codes to it.
Codes in file-2:
AnimatedOpacity
Enter AnimatedOpacity. This widget builds on the Opacity widget by adding implicit animation support. How do we use it? Remember our scenario: we need to show a next button with animated visibility. We wrap the button widget inside the AnimatedOpacity widget, feed in some proper values and add a condition to trigger the animation — and Flutter can handle the rest.
_getAnimatedOpacityButton() {return AnimatedOpacity(duration: Duration(seconds: 1),curve: Curves.easeInOut,opacity: showNextButton ? 1 : 0,child: _getButton(),);}
AnimatedCrossFade
This widget requires two children, as the widget crossfades between them based on some condition. One important thing to keep in mind while using this widget is that both of the children should be the same width. If the height is different, then the taller widget gets clipped from the bottom. In this scenario, two widgets will be used as children: input and label.
_getAnimatedCrossfade() {return AnimatedCrossFade(duration: Duration(seconds: 1),alignment: Alignment.center,reverseDuration: Duration(seconds: 1),firstChild: _getNameInputWidget(),firstCurve: Curves.easeInOut,secondChild: _getNameLabelWidget(),secondCurve: Curves.easeInOut,crossFadeState: showNameLabel ? CrossFadeState.showSecond : CrossFadeState.showFirst,);}
AnimatedAlign
As always, several techniques can be used to achieve this. Since the name label widget is already center-aligned, animating its alignment would be much simpler than manipulating the top and left values of the widget. The AnimatedAlign
widget is perfect for this job.
To initiate this animation, a trigger is required. The sole purpose of this widget is to animate alignment change, so it has only a few properties: add a child, set its alignment, trigger the alignment change, and that’s it.
_getAnimatedAlignWidget() {return AnimatedAlign(duration: Duration(seconds: 1),curve: Curves.easeInOut,alignment: alignTop ? Alignment.topLeft : Alignment.center,child: _getAnimatedCrossfade(),);
}
AnimatedPadding
Now we have the user’s name at the top, smoothly animated without much effort, using different kinds of animated widgets. Let’s add a greeting, “Hi,” before the name. Adding a text widget with value “Hi,” at the top will make it overlap the greeting text widget, looking like the image below.
What if the name text widget had some padding on the left? Increasing the left padding will definitely work, but wait: can we increase the padding with some animation? Yes, and that is what AnimatedPadding
does. To make all this much better looking, let’s have the greetings text widget fade in and the name text widget’s padding increase at the same time.
_getAnimatedPaddingWidget() {return AnimatedPadding(duration: Duration(seconds: 1),curve: Curves.fastOutSlowIn,padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0),child: _getAnimatedCrossfade(),);}
Future.delayed(Duration(seconds: 1), (){sum = a + b; // This sum will be calculated after 1 second.print(sum);});Since the delay duration is already known (calculated from previous animation durations), the animation can be triggered after this interval.// Showing “Hi” after 1 second — greetings visibility trigger._showGreetings() {Future.delayed(Duration(seconds: 1), () {setState(() {showGreetings = true;});});}// Increasing the padding for name label widget after 1 second — increase padding trigger._increaseLeftPadding() {Future.delayed(Duration(seconds: 1), () {setState(() {increaseLeftPadding = true;});});}
AnimatedSize
The AnimatedSize
widget requires a property — vsync — which accepts a ticker provider. The easiest way to get a ticker provider is to add a Mixin to the class. There are two basic ticker provider implementations: SingleTickerProviderStateMixin
, which provides a single ticker; and TickerProviderStateMixin
, which provides several.
The default implementation of a Ticker is used to mark the frames of an animation. In this case, the latter is employed.
// Helper method to create quotes card widget._getQuoteCardWidget() {return Card(color: Colors.green,elevation: 8.0,child: _getAnimatedSizeWidget(),);}// Helper method to create animated size widget and set its properties._getAnimatedSizeWidget() {return AnimatedSize(duration: Duration(seconds: 1),curve: Curves.easeInOut,vsync: this,child: _getQuoteContainer(),);}// Helper method to create the quotes container widget with different sizes._getQuoteContainer() {return Container(height: showQuoteCard ? 100 : 0,width: showQuoteCard ? screenWidth - 32 : 0,child: Center(child: Padding(padding: EdgeInsets.symmetric(horizontal: 16),child: Text(quote, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 14),),),
),);}// Trigger used to show the quote card widget._showQuote() {Future.delayed(Duration(seconds: 2), () {setState(() {showQuoteCard = true;});
});
}
AnimatedPositioned
This widget automatically transitions the child’s position over a given duration whenever the specified position changes. One point to note: it works only if its parent widget is a “Stack.” This widget is pretty simple and straightforward to use.
_getAnimatedPositionWidget() {return AnimatedPositioned(duration: Duration(seconds: 1),curve: Curves.easeInOut,child: _getQuoteCardWidget(),top: showQuoteCard ? screenHeight/2 - 100 : screenHeight,left: !showQuoteCard ? screenWidth/2 : 12,
);
}
Codes in file-3:
The description.dart file contains a list of all the hardcoded quotations. One point to note here is that the list is a static object. This means it can be used at other places without creating a new object of the Quotes class. This is chosen by design, as the above list act simply as a utility.
Add the following code to this file:
class Quotes {static const quotesArray = ["Good, better, best. Never let it rest. 'Till your good is better and your better is best","It does not matter how slowly you go as long as you do not stop.”"Only I can change my life. No one can do it for me."
];
}
Conclusion
Animations are an integral part of user experience. Static apps or apps with janky animation not only lower user retention but also a developer’s reputation to deliver results.
Today, most popular apps have some kind of subtle animation to delight users. Animated feedback to user requests can also engage them to explore more. Flutter offers a lot of features for cross-platform development, including rich support for smooth and responsive animations.