Exploring Flutter Animations: From Simple to Complex

MD. Sad Adnan
4 min readMar 3, 2023

--

Flutter provides a wide range of tools for creating stunning animations in your app. From simple fade-ins to complex physics-based animations, there are endless possibilities for adding visual flair to your user interface. In this article, we’ll explore some of the basics of animation in Flutter and work our way up to more complex examples.

  1. Getting Started with AnimationController The AnimationController class is at the heart of all Flutter animations. It provides a way to create animations that can be controlled and manipulated over time. Here's an example of using an AnimationController to create a simple fade-in animation:
class FadeInAnimation extends StatefulWidget {
final Widget child;
final Duration duration;

FadeInAnimation({required this.child, this.duration = const Duration(milliseconds: 500)});

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

class _FadeInAnimationState extends State<FadeInAnimation> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _opacityAnimation;

@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration);
_opacityAnimation = Tween<double>(begin: 0, end: 1).animate(_controller);
_controller.forward();
}

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

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _opacityAnimation,
builder: (BuildContext context, Widget? child) {
return Opacity(
opacity: _opacityAnimation.value,
child: widget.child,
);
},
);
}
}

This code creates a custom FadeInAnimation widget that takes a child widget and a duration as parameters. It uses an AnimationController to control the opacity of the child widget over time. The AnimatedBuilder widget is used to rebuild the UI whenever the animation updates.

2. Animating Widgets with AnimatedWidget Another way to create animations in Flutter is to use the AnimatedWidget class. This class provides a simple way to animate the properties of a widget. Here's an example of using AnimatedWidget to create a simple color animation:

class ColorAnimation extends AnimatedWidget {
final Widget child;

ColorAnimation({required Animation<Color?> animation, required this.child})
: super(listenable: animation);

@override
Widget build(BuildContext context) {
final Animation<Color?> animation = listenable as Animation<Color?>;
return ColorFiltered(
colorFilter: ColorFilter.mode(animation.value!, BlendMode.modulate),
child: child,
);
}
}

class ColorAnimationExample extends StatefulWidget {
@override
_ColorAnimationExampleState createState() => _ColorAnimationExampleState();
}

class _ColorAnimationExampleState extends State<ColorAnimationExample>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Color?> _colorAnimation;

@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(seconds: 2));
_colorAnimation =
ColorTween(begin: Colors.red, end: Colors.blue).animate(_controller);
_controller.repeat(reverse: true);
}

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

@override
Widget build(BuildContext context) {
return Center(
child: ColorAnimation(
animation: _colorAnimation,
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
shape: BoxShape.circle),
child: Center(
child: Text(
"Hello, World!",
style: TextStyle(fontSize: 24),
),
),
),
),
);
}
}

In this code, we create a custom `ColorAnimation` widget that takes an `Animation<Color>` object and a child widget. The `AnimatedWidget` superclass handles updating the widget whenever the animation changes. The `ColorFiltered` widget is used to apply a color filter to the child widget based on the value of the animation.

3. Working with Curves and Animations
Animations in Flutter can be customized with curves, which define how the animation progresses over time. There are several built-in curves available, such as `Curves.linear` and `Curves.easeInOut`. Here’s an example of using a custom curve to create a bouncy animation:

class BouncyAnimation extends StatefulWidget {
final Widget child;
final Duration duration;

BouncyAnimation({required this.child, this.duration = const Duration(milliseconds: 500)});

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

class _BouncyAnimationState extends State<BouncyAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this, duration: widget.duration, lowerBound: -0.5, upperBound: 1.0);
_animation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.bounceOut,
),
);
_controller.forward();
}

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

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (BuildContext context, Widget? child) {
return Transform.scale(
scale: 1 + _animation.value,
child: widget.child,
);
},
);
}
}

In this code, we create a custom `BouncyAnimation` widget that uses the `CurvedAnimation` class to apply a bouncy curve to the animation. The `Transform.scale` widget is used to scale the child widget based on the value of the animation.

4. Animating Complex Widgets with CustomPaint
Sometimes, built-in widgets aren’t enough to create the animation you want. In those cases, you can use the `CustomPaint` widget to create your own custom animations. Here’s an example of using `CustomPaint` to create a rotating gradient animation:

class GradientAnimation extends StatefulWidget {
final Widget child;
final Duration duration;

GradientAnimation({required this.child, this.duration = const Duration(seconds: 2)});

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

class _GradientAnimationState extends State<GradientAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;

@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration)
..repeat();
_animation = Tween<double>(begin: 0, end: 2 * math.pi).animate(_controller);
}

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

@override
Widget build(BuildContext context) {
return CustomPaint(
painter: GradientPainter(_animation),
child: widget.child,
);
}
}
class GradientPainter extends CustomPainter {
final Animation<double> animation;

GradientPainter(this.animation) : super(repaint: animation);

@override
void paint(Canvas canvas, Size size) {
final rect = Offset.zero & size;
final gradient = SweepGradient(
center: Alignment.center,
colors: [
Colors.red,
Colors.green,
Colors.blue,
],
startAngle: animation.value,
endAngle: animation.value + math.pi / 2,
);
canvas.drawPaint(Paint()..shader = gradient.createShader(rect));
}

@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}

In this code, we create a custom `GradientAnimation` widget that uses the `CustomPaint` widget to draw a rotating gradient on top of the child widget. The `GradientPainter` class defines the actual painting logic for the animation and is given an `Animation<double>` object to control the rotation of the gradient.

Conclusion
Flutter animations can be simple or complex, depending on your needs. By understanding the basics of animations, using pre-built widgets like `AnimatedBuilder` and `CustomPaint`, and customizing animations with curves and controllers, you can create beautiful and engaging animations in your Flutter apps. Whether you’re building a simple app or a complex one, animations can help elevate the user experience and make your app stand out from the rest.

--

--

MD. Sad Adnan
MD. Sad Adnan

Written by MD. Sad Adnan

Love Programming, Developing Solutions of Real Life Problems and Reading Books.

No responses yet