lerp method Null safety
Linearly interpolate between two borders.
If a border is null, it is treated as having four BorderSide.none borders.
This supports interpolating between Border and BorderDirectional
objects. If both objects are different types but both have sides on one or
both of their lateral edges (the two sides that aren't the top and bottom)
other than BorderSide.none, then the sides are interpolated by reducing
a
's lateral edges to BorderSide.none over the first half of the
animation, and then bringing b
's lateral edges from BorderSide.none
over the second half of the animation.
For a more flexible approach, consider ShapeBorder.lerp, which would instead add the two sets of sides and interpolate them simultaneously.
The t
argument represents position on the timeline, with 0.0 meaning
that the interpolation has not started, returning a
(or something
equivalent to a
), 1.0 meaning that the interpolation has finished,
returning b
(or something equivalent to b
), and values in between
meaning that the interpolation is at the relevant point on the timeline
between a
and b
. The interpolation can be extrapolated beyond 0.0 and
1.0, so negative values and values greater than 1.0 are valid (and can
easily be generated by curves such as Curves.elasticInOut).
Values for t
are usually obtained from an Animation<double>, such as
an AnimationController.
Implementation
static BoxBorder? lerp(BoxBorder? a, BoxBorder? b, double t) {
assert(t != null);
if ((a is Border?) && (b is Border?)) {
return Border.lerp(a, b, t);
}
if ((a is BorderDirectional?) && (b is BorderDirectional?)) {
return BorderDirectional.lerp(a, b, t);
}
if (b is Border && a is BorderDirectional) {
final BoxBorder c = b;
b = a;
a = c;
t = 1.0 - t;
// fall through to next case
}
if (a is Border && b is BorderDirectional) {
if (b.start == BorderSide.none && b.end == BorderSide.none) {
// The fact that b is a BorderDirectional really doesn't matter, it turns out.
return Border(
top: BorderSide.lerp(a.top, b.top, t),
right: BorderSide.lerp(a.right, BorderSide.none, t),
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
left: BorderSide.lerp(a.left, BorderSide.none, t),
);
}
if (a.left == BorderSide.none && a.right == BorderSide.none) {
// The fact that a is a Border really doesn't matter, it turns out.
return BorderDirectional(
top: BorderSide.lerp(a.top, b.top, t),
start: BorderSide.lerp(BorderSide.none, b.start, t),
end: BorderSide.lerp(BorderSide.none, b.end, t),
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
);
}
// Since we have to swap a visual border for a directional one,
// we speed up the horizontal sides' transitions and switch from
// one mode to the other at t=0.5.
if (t < 0.5) {
return Border(
top: BorderSide.lerp(a.top, b.top, t),
right: BorderSide.lerp(a.right, BorderSide.none, t * 2.0),
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
left: BorderSide.lerp(a.left, BorderSide.none, t * 2.0),
);
}
return BorderDirectional(
top: BorderSide.lerp(a.top, b.top, t),
start: BorderSide.lerp(BorderSide.none, b.start, (t - 0.5) * 2.0),
end: BorderSide.lerp(BorderSide.none, b.end, (t - 0.5) * 2.0),
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
);
}
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('BoxBorder.lerp can only interpolate Border and BorderDirectional classes.'),
ErrorDescription(
'BoxBorder.lerp() was called with two objects of type ${a.runtimeType} and ${b.runtimeType}:\n'
' $a\n'
' $b\n'
'However, only Border and BorderDirectional classes are supported by this method.',
),
ErrorHint('For a more general interpolation method, consider using ShapeBorder.lerp instead.'),
]);
}