lerp static method
Interpolate between two text styles for animated transitions.
Interpolation will not work well if the styles don't specify the same fields.
When this happens, to keep the interpolated transition smooth, the
implementation uses the non-null value throughout the transition for
lerpable fields such as colors (for example, if one TextStyle specified
fontSize
but the other didn't, the returned TextStyle will use the
fontSize
from the TextStyle that specified it, regardless of the t
value).
This method throws when the given TextStyles don't have the same inherit value and a lerpable field is missing from both TextStyles, as that could result in jumpy transitions.
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.
If foreground is specified on either of a
or b
, both will be treated
as if they have a foreground paint (creating a new Paint if necessary
based on the color property).
If background is specified on either of a
or b
, both will be treated
as if they have a background paint (creating a new Paint if necessary
based on the backgroundColor property).
Implementation
static TextStyle? lerp(TextStyle? a, TextStyle? b, double t) {
if (identical(a, b)) {
return a;
}
String? lerpDebugLabel;
assert(() {
lerpDebugLabel = 'lerp(${a?.debugLabel ?? _kDefaultDebugLabel} ⎯${t.toStringAsFixed(1)}→ ${b?.debugLabel ?? _kDefaultDebugLabel})';
return true;
}());
if (a == null) {
return TextStyle(
inherit: b!.inherit,
color: Color.lerp(null, b.color, t),
backgroundColor: Color.lerp(null, b.backgroundColor, t),
fontSize: t < 0.5 ? null : b.fontSize,
fontWeight: FontWeight.lerp(null, b.fontWeight, t),
fontStyle: t < 0.5 ? null : b.fontStyle,
letterSpacing: t < 0.5 ? null : b.letterSpacing,
wordSpacing: t < 0.5 ? null : b.wordSpacing,
textBaseline: t < 0.5 ? null : b.textBaseline,
height: t < 0.5 ? null : b.height,
leadingDistribution: t < 0.5 ? null : b.leadingDistribution,
locale: t < 0.5 ? null : b.locale,
foreground: t < 0.5 ? null : b.foreground,
background: t < 0.5 ? null : b.background,
shadows: t < 0.5 ? null : b.shadows,
fontFeatures: t < 0.5 ? null : b.fontFeatures,
fontVariations: lerpFontVariations(null, b.fontVariations, t),
decoration: t < 0.5 ? null : b.decoration,
decorationColor: Color.lerp(null, b.decorationColor, t),
decorationStyle: t < 0.5 ? null : b.decorationStyle,
decorationThickness: t < 0.5 ? null : b.decorationThickness,
debugLabel: lerpDebugLabel,
fontFamily: t < 0.5 ? null : b._fontFamily,
fontFamilyFallback: t < 0.5 ? null : b._fontFamilyFallback,
package: t < 0.5 ? null : b._package,
overflow: t < 0.5 ? null : b.overflow,
);
}
if (b == null) {
return TextStyle(
inherit: a.inherit,
color: Color.lerp(a.color, null, t),
backgroundColor: Color.lerp(null, a.backgroundColor, t),
fontSize: t < 0.5 ? a.fontSize : null,
fontWeight: FontWeight.lerp(a.fontWeight, null, t),
fontStyle: t < 0.5 ? a.fontStyle : null,
letterSpacing: t < 0.5 ? a.letterSpacing : null,
wordSpacing: t < 0.5 ? a.wordSpacing : null,
textBaseline: t < 0.5 ? a.textBaseline : null,
height: t < 0.5 ? a.height : null,
leadingDistribution: t < 0.5 ? a.leadingDistribution : null,
locale: t < 0.5 ? a.locale : null,
foreground: t < 0.5 ? a.foreground : null,
background: t < 0.5 ? a.background : null,
shadows: t < 0.5 ? a.shadows : null,
fontFeatures: t < 0.5 ? a.fontFeatures : null,
fontVariations: lerpFontVariations(a.fontVariations, null, t),
decoration: t < 0.5 ? a.decoration : null,
decorationColor: Color.lerp(a.decorationColor, null, t),
decorationStyle: t < 0.5 ? a.decorationStyle : null,
decorationThickness: t < 0.5 ? a.decorationThickness : null,
debugLabel: lerpDebugLabel,
fontFamily: t < 0.5 ? a._fontFamily : null,
fontFamilyFallback: t < 0.5 ? a._fontFamilyFallback : null,
package: t < 0.5 ? a._package : null,
overflow: t < 0.5 ? a.overflow : null,
);
}
assert(() {
if (a.inherit == b.inherit) {
return true;
}
final List<String> nullFields = <String>[
if (a.foreground == null && b.foreground == null && a.color == null && b.color == null) 'color',
if (a.background == null && b.background == null && a.backgroundColor == null && b.backgroundColor == null) 'backgroundColor',
if (a.fontSize == null && b.fontSize == null) 'fontSize',
if (a.letterSpacing == null && b.letterSpacing == null) 'letterSpacing',
if (a.wordSpacing == null && b.wordSpacing == null) 'wordSpacing',
if (a.height == null && b.height == null) 'height',
if (a.decorationColor == null && b.decorationColor == null) 'decorationColor',
if (a.decorationThickness == null && b.decorationThickness == null) 'decorationThickness',
];
if (nullFields.isEmpty) {
return true;
}
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Failed to interpolate TextStyles with different inherit values.'),
ErrorSpacer(),
ErrorDescription('The TextStyles being interpolated were:'),
a.toDiagnosticsNode(name: 'from', style: DiagnosticsTreeStyle.singleLine),
b.toDiagnosticsNode(name: 'to', style: DiagnosticsTreeStyle.singleLine),
ErrorDescription(
'The following fields are unspecified in both TextStyles:\n'
'${nullFields.map((String name) => '"$name"').join(', ')}.\n'
'When "inherit" changes during the transition, these fields may '
'observe abrupt value changes as a result, causing "jump"s in the '
'transition.'
),
ErrorSpacer(),
ErrorHint(
'In general, TextStyle.lerp only works well when both TextStyles have '
'the same "inherit" value, and specify the same fields.',
),
ErrorHint(
'If the TextStyles were directly created by you, consider bringing '
'them to parity to ensure a smooth transition.'
),
ErrorSpacer(),
ErrorHint(
'If one of the TextStyles being lerped is significantly more elaborate '
'than the other, and has "inherited" set to false, it is often because '
'it is merged with another TextStyle before being lerped. Comparing '
'the "debugLabel"s of the two TextStyles may help identify if that was '
'the case.'
),
ErrorHint(
'For example, you may see this error message when trying to lerp '
'between "ThemeData()" and "Theme.of(context)". This is because '
'TextStyles from "Theme.of(context)" are merged with TextStyles from '
'another theme and thus are more elaborate than the TextStyles from '
'"ThemeData()" (which is reflected in their "debugLabel"s -- '
'TextStyles from "Theme.of(context)" should have labels in the form of '
'"(<A TextStyle>).merge(<Another TextStyle>)"). It is recommended to '
'only lerp ThemeData with matching TextStyles.'
),
]);
}());
return TextStyle(
inherit: t < 0.5 ? a.inherit : b.inherit,
color: a.foreground == null && b.foreground == null ? Color.lerp(a.color, b.color, t) : null,
backgroundColor: a.background == null && b.background == null ? Color.lerp(a.backgroundColor, b.backgroundColor, t) : null,
fontSize: ui.lerpDouble(a.fontSize ?? b.fontSize, b.fontSize ?? a.fontSize, t),
fontWeight: FontWeight.lerp(a.fontWeight, b.fontWeight, t),
fontStyle: t < 0.5 ? a.fontStyle : b.fontStyle,
letterSpacing: ui.lerpDouble(a.letterSpacing ?? b.letterSpacing, b.letterSpacing ?? a.letterSpacing, t),
wordSpacing: ui.lerpDouble(a.wordSpacing ?? b.wordSpacing, b.wordSpacing ?? a.wordSpacing, t),
textBaseline: t < 0.5 ? a.textBaseline : b.textBaseline,
height: ui.lerpDouble(a.height ?? b.height, b.height ?? a.height, t),
leadingDistribution: t < 0.5 ? a.leadingDistribution : b.leadingDistribution,
locale: t < 0.5 ? a.locale : b.locale,
foreground: (a.foreground != null || b.foreground != null)
? t < 0.5
? a.foreground ?? (Paint()..color = a.color!)
: b.foreground ?? (Paint()..color = b.color!)
: null,
background: (a.background != null || b.background != null)
? t < 0.5
? a.background ?? (Paint()..color = a.backgroundColor!)
: b.background ?? (Paint()..color = b.backgroundColor!)
: null,
shadows: ui.Shadow.lerpList(a.shadows, b.shadows, t),
fontFeatures: t < 0.5 ? a.fontFeatures : b.fontFeatures,
fontVariations: lerpFontVariations(a.fontVariations, b.fontVariations, t),
decoration: t < 0.5 ? a.decoration : b.decoration,
decorationColor: Color.lerp(a.decorationColor, b.decorationColor, t),
decorationStyle: t < 0.5 ? a.decorationStyle : b.decorationStyle,
decorationThickness: ui.lerpDouble(a.decorationThickness ?? b.decorationThickness, b.decorationThickness ?? a.decorationThickness, t),
debugLabel: lerpDebugLabel,
fontFamily: t < 0.5 ? a._fontFamily : b._fontFamily,
fontFamilyFallback: t < 0.5 ? a._fontFamilyFallback : b._fontFamilyFallback,
package: t < 0.5 ? a._package : b._package,
overflow: t < 0.5 ? a.overflow : b.overflow,
);
}