Fixing BoxDecoration background animation in Flutter

Wenkai Fan
ITNEXT
Published in
3 min readMar 18, 2022

--

Container and BoxDecoration are two classes widely used for Flutter development. If you want something more than just a colored box, you would write something like:

Widget container = Container(
decoration: BoxDecoration(color: color, gradient: gradient, image: image),
child: child,
);

There are other properties in BoxDecoration such as shadow and border but I’ll focus on the background animation in this article.

You can see there are three types of background you can set in BoxDecoration: color, gradient, and image. They are painted from bottom to top in this order. If you want to animate between two BoxDecoration, you can use the DecorationTween class:

intermediate = DecorationTween(begin: begin, end: end, t);

where t means the progress of your animation (between 0 and 1).

You can also just use the AnimatedContainer widget and provide it with the current BoxDecoration you want and it will animate automatically from the previous one to the current one:

Widget container = AnimatedContainer(
duration: duration,
curve: curbe,
decoration: decoration,
child: child
);

The interface of AnimatedContainer is almost the same as the Container class except for the two additional parameters duration and curve specifying the details of the animation.

All these sound great and simple to use. If all you want to do is to animate between two colors or two gradients with the same type (there are three types of gradients in Flutter), you can use the AnimatedContainer class without any issue. But it will give you a weird white-colored intermediate background in other scenarios.

Below is the actual implementation for animating between two BoxDecoration:

static BoxDecoration? lerp(BoxDecoration? a, BoxDecoration? b, double t) {
...
return BoxDecoration(
color: Color.lerp(a.color, b.color, t),
image: t < 0.5 ? a.image : b.image, // TODO(ianh): cross-fade the image
border: BoxBorder.lerp(a.border, b.border, t),
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, b.borderRadius, t),
boxShadow: BoxShadow.lerpList(a.boxShadow, b.boxShadow, t),
gradient: Gradient.lerp(a.gradient, b.gradient, t),
shape: t < 0.5 ? a.shape : b.shape,
);
}

You can immediately see the problem. For animating between two image backgrounds, the current implementation just changes the background from the first image to the second one at 50% progress without cross-fade. I have been staring at this TODO comment for around a year and finally decided to do it myself. And for lesser-used scenarios like animating between a color and a gradient or animating between two gradients of different types, the current implementation also does not work well.

That's why I implemented the SmoothDecorationTween and SmoothAnimatedContainer class. They have the same interface as their built-in counterparts. Let's see some comparisons:

Left: AnimatedContainer, Right: SmoothAnimatedContainer
Left: AnimatedContainer, Right: SmoothAnimatedContainer
Left: AnimatedContainer, Right: SmoothAnimatedContainer

I have published the package on pub.dev. Check it out if you need such features in your app and happy coding in Flutter!

--

--

Ph.D. in Nuclear Physics, M.S. in Computer Science at Duke University, Flutter lover