Advanced animation techniques in Flutter

Raju Potharaju
GYTWorkz
Published in
5 min readFeb 15, 2023
Fig: Blog Banner

In today’s world, where mobile apps have become an essential part of our daily lives, it’s crucial to build visually appealing and engaging user interfaces (UIs) that captivate users’ attention.

Flutter provides a rich set of tools and techniques for creating engaging and visually appealing animations.

In this blog post, we will explore some advanced animation techniques in Flutter and provide examples of how they can be used to create stunning UIs.

1. Custom Animations with CustomPaint

The CustomPaint widget is a powerful tool that allows developers to create custom animations by drawing on a canvas. With this widget, we can create complex animations that involve shapes, curves, and gradients. Let's take a look at an example where we create a simple animation that draws a sun like an arc with gradient colors.

import 'dart:math';
import 'package:flutter/material.dart';

class CustomPaintAnimation extends StatefulWidget {
@override
_CustomPaintAnimationState createState() => _CustomPaintAnimationState();
}

class _CustomPaintAnimationState extends State<CustomPaintAnimation>
with SingleTickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
)..repeat();
}

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

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedBuilder(
animation: controller,
builder: (context, _) {
return CustomPaint(
painter: CirclePainter(
controller,
),
child: Container(),
);
})),
);
}
}

class CirclePainter extends CustomPainter {
final Animation<double> animation;

CirclePainter(
this.animation,
);

@override
void paint(Canvas canvas, Size size) {
double percentage = animation.value * 100;
double radius = 100.0;
final c = Offset(size.width / 2, size.height / 2);
drawCircle(canvas, c, radius);
drawArc(canvas, c, radius, percentage);
}

void drawCircle(Canvas canvas, Offset c, double radius) {
Paint paint = Paint()
..strokeWidth = 1
..style = PaintingStyle.stroke
..color = Colors.orange;
canvas.drawCircle(c, radius, paint);
}

void drawArc(Canvas canvas, Offset center, double radius, double percentage) {
Rect rect =
Rect.fromCenter(center: center, width: 2 * radius, height: 2 * radius);
Paint paint = Paint()
..shader = RadialGradient(
colors: [
Colors.white,
Colors.yellow,
Colors.orange,
Colors.red.shade400
],
).createShader(rect)
..color = Colors.orangeAccent
..strokeWidth = 10
..strokeCap = StrokeCap.round;

const startAngle = -90 * pi / 180;
final sweepAngle = (360 * percentage * 0.01) * pi / 180;
canvas.drawArc(rect, startAngle, sweepAngle, true, paint);
}

@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Eg. Sun drawn with CustomPaint

2. Interactive Animations with Gestures
Flutter allows to create interactive animations with gestures. Gestures can be used to create animations that respond to user input, such as swiping or tapping. Let’s take a look at an example where we create an animation that scales up a widget when the user taps on it.


class InteractiveAnimation extends StatefulWidget {
// const InteractiveAnimation({super.key});

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

class _InteractiveAnimationState extends State<InteractiveAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scale;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_scale = Tween<double>(begin: 1.0, end: 1.5).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
),
);
}

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

void _onTap() {
_controller.forward().then((value) => _controller.reverse());
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTap: _onTap,
child: AnimatedBuilder(
animation: _scale,
builder: (context, child) {
return Transform.scale(
scale: _scale.value,
child: child,
);
},
child: Container(
width: 200.0,
height: 200.0,
color: Colors.orangeAccent,
),
),
),
),
);
}
}
Eg: Resize Widgets on tap

In this example, we use a `GestureDetector` widget to detect taps on the widget. When the user taps on the widget, the _onTap function is called, which starts the animation. The animation scales up the widget to 1.5 times its original size, and then immediately scales it back down to its original size. This creates a nice interactive effect that responds to user input.

2. Staggered Animations with Staggered Animation Builder

The StaggeredAnimationBuilder widget is a tool that allows developers to create staggered animations. Staggered animations are animations that occur at different times, which can create a dynamic and engaging user interface. Let's take a look at an example where we create a staggered animation that animates the opacity of a group of widgets.

import 'package:flutter/material.dart';

class StaggeredAnimation extends StatefulWidget {
@override
_StaggeredAnimationState createState() => _StaggeredAnimationState();
}

class _StaggeredAnimationState extends State<StaggeredAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _opacity;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..forward();
_opacity = Tween<double>(begin: 0.0, end: 1.0).animate(CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.5, curve: Curves.easeIn),
));
}

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

final colorsList = [
Colors.green.shade300,
Colors.green.shade500,
Colors.green.shade700,
Colors.green.shade900
];

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StaggeredAnimationBuilder(
duration: const Duration(milliseconds: 500),
delay: const Duration(milliseconds: 300),
builder: (context, index) {
return Container(
width: 200,
height: 100,
margin: const EdgeInsets.symmetric(vertical: 4),
color: colorsList[index],
child: Center(
child: FadeTransition(
opacity: _opacity,
child: Text('Widget $index'),
),
),
);
},
),
),
);
}
}

class StaggeredAnimationBuilder extends StatefulWidget {
final int count;
final Widget Function(BuildContext, int) builder;
final Duration duration;
final Duration delay;

const StaggeredAnimationBuilder({
this.count = 4,
required this.builder,
this.duration = const Duration(milliseconds: 400),
this.delay = const Duration(milliseconds: 200),
});

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

class _StaggeredAnimationBuilderState extends State<StaggeredAnimationBuilder>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late List<Animation<double>> _animations;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: widget.duration * widget.count,
)..forward();
_animations = List.generate(widget.count, (index) {
return Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(
(index * widget.delay.inMilliseconds) /
_controller.duration!.inMilliseconds,
1.0,
curve: Curves.easeInOut,
),
),
);
});
}

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

@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: List.generate(widget.count, (index) {
return AnimatedBuilder(
animation: _animations[index],
builder: (context, child) {
return Opacity(
opacity: _animations[index].value,
child: widget.builder(context, index),
);
},
);
}),
);
}
}
Eg: List of Widgets in Staggered Opacity

Conclusion Flutter provides a powerful animation framework that makes it easy to create beautiful and engaging user interfaces. With the help of the AnimatedBuilder, AnimatedContainer, and AnimatedOpacity widgets, we can create complex animations with very little code. Additionally, Flutter provides more advanced animation techniques, such as the StaggeredAnimationBuilder and interactive animations with gestures, that can be used to create even more engaging user interfaces.

By using Flutter’s animation framework, developers can create user interfaces that are not only visually appealing, but also highly interactive and engaging. With the examples provided in this article, we can get started with Flutter animations and take the user interface designs to the next level.

If you find this blog informative do give a clap below.

Connect with me on LinkedIn for more flutter related content.

--

--

Raju Potharaju
GYTWorkz

Software Engineer with 3 years of experience building beautiful and complex Applications | Loves Teaching | Talks about Flutter, React, Python and Java