DEV Community

Dominik Roszkowski
Dominik Roszkowski

Posted on • Originally published at roszkowski.dev on

Using custom curve in Flutter Hero animations

Sometimes the default Hero behavior is not enough and you’d like to add some custom taste to it. One of the simple ways to do it is to customize curve of the transition animation.

In this post you’ll see a sample that allows to use any custom curve to animate bounds of the Hero. In my case it was a bit faster expansion in vertical axis.

Comparison

Contents

  • Hero properties
  • What is createRectTween
  • How to change the default implementation

Hero properties

Hero class has several convenient properties that allow to customize its behavior. These are:

  • flightShuttleBuilder that allows to wrap or change entire Hero animation by e.g. adding rotation or fade transition
  • placeholderBuilder that gives us possibility to show Widget in place previously occupied by the animated widget
  • createRectTween which is a function that determines the bounds of the Hero during animation - that’s what we want to customize today

What is createRectTween

This property takes a CreateRectTween function which is simply a typedef for Tween<Rect> Function(Rect begin, Rect end).

It means that it interpolates two Rects between begin and end state. By default a RectTween(begin: begin, end: end) is used in Hero. It uses its lerp method which aside from few asserts only interpolates Rect coordinates in linear manner:

static Rect lerp(Rect a, Rect b, double t) {
  assert(t != null);
  if (a == null && b == null)
    return null;
  if (a == null)
    return Rect.fromLTRB(b.left * t, b.top * t, b.right * t, b.bottom * t);
  if (b == null) {
    final double k = 1.0 - t;
    return Rect.fromLTRB(a.left * k, a.top * k, a.right * k, a.bottom * k);
  }
  return Rect.fromLTRB(
    lerpDouble(a.left, b.left, t),
    lerpDouble(a.top, b.top, t),
    lerpDouble(a.right, b.right, t),
    lerpDouble(a.bottom, b.bottom, t),
  );
}

Enter fullscreen mode Exit fullscreen mode

The method lerpDouble is where the interpolation takes place:

double lerpDouble(num a, num b, double t) {
    if (a == null && b == null) return null;
    a ??= 0.0;
    b ??= 0.0;
    return a + (b - a) * t;
}

Enter fullscreen mode Exit fullscreen mode

How to change the default implementation

When defining your Hero you should return a custom createRectTween function like this:

Hero(
    tag: index,
    createRectTween: (begin, end) {
        return CustomRectTween(a: begin, b: end);
    },
    child: Poster(),
),

Enter fullscreen mode Exit fullscreen mode

And finally CustomRectTween has to extend RectTween and to have overwritten lerp method.

The parameter t is animation clock value between 0.0 and 1.0. Each Curve can be used to interpolate any given value between 0.0 and 1.0 on the curve. I use my custom cubic curve in this example.

class CustomRectTween extends RectTween {
  CustomRectTween({this.a, this.b}) : super(begin: a, end: b);
  final Rect a;
  final Rect b;

  @override
  Rect lerp(double t) {
    Curves.elasticOut.transform(t);
    //any curve can be applied here e.g. Curve.elasticOut.transform(t);
    final verticalDist = Cubic(0.72, 0.15, 0.5, 1.23).transform(t);

    final top = lerpDouble(a.top, b.top, t) * (1 - verticalDist);
    return Rect.fromLTRB(
      lerpDouble(a.left, b.left, t),
      top,
      lerpDouble(a.right, b.right, t),
      lerpDouble(a.bottom, b.bottom, t),
    );
  }

  double lerpDouble(num a, num b, double t) {
    if (a == null && b == null) return null;
    a ??= 0.0;
    b ??= 0.0;
    return a + (b - a) * t;
  }
}

Enter fullscreen mode Exit fullscreen mode

It’s important to note that I wanted to change only top coordinate of the Hero’s Rect, so when calculating the value I use (1 - verticalDist) (thus inverting the curve).

I hope you’ll find it useful and try to experiment with Hero widget on your own.

I highly recommend the post Mastering Hero Animations in Flutter by Chema Molins from Flutter Community.

Discussion (0)