From ecb708ee3a76e8f533621e1b8332dcdf576e85bc Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Thu, 21 Dec 2017 11:09:49 -0800 Subject: [PATCH] Clean up lerp() methods and their documentation. (#13684) This cleans up lerp, lerpFrom, lerpTo, and scale methods, and their documentation. Fixes https://github.com/flutter/flutter/issues/12377. --- packages/flutter/lib/src/cupertino/route.dart | 32 +++-- .../flutter/lib/src/material/theme_data.dart | 79 ++++++----- .../flutter/lib/src/material/typography.dart | 41 ++++-- .../flutter/lib/src/painting/alignment.dart | 39 ++++++ .../lib/src/painting/border_radius.dart | 39 ++++++ .../flutter/lib/src/painting/borders.dart | 123 +++++++++++++++--- .../flutter/lib/src/painting/box_border.dart | 75 ++++++----- .../lib/src/painting/box_decoration.dart | 13 ++ .../flutter/lib/src/painting/box_shadow.dart | 34 ++++- packages/flutter/lib/src/painting/colors.dart | 25 +++- .../flutter/lib/src/painting/decoration.dart | 71 +++++++--- .../flutter/lib/src/painting/edge_insets.dart | 41 +++++- .../lib/src/painting/flutter_logo.dart | 22 +++- .../lib/src/painting/fractional_offset.dart | 13 ++ .../flutter/lib/src/painting/gradient.dart | 94 ++++++++++--- .../painting/rounded_rectangle_border.dart | 3 + .../lib/src/painting/shape_decoration.dart | 18 ++- .../lib/src/painting/stadium_border.dart | 6 + .../flutter/lib/src/painting/text_style.dart | 53 +++++--- packages/flutter/lib/src/rendering/box.dart | 13 ++ packages/flutter/lib/src/rendering/stack.dart | 15 ++- .../lib/src/rendering/table_border.dart | 43 ++++-- .../lib/src/widgets/icon_theme_data.dart | 21 ++- .../flutter/test/material/theme_test.dart | 2 +- .../test/painting/text_style_test.dart | 12 +- .../test/rendering/table_border_test.dart | 4 +- 26 files changed, 738 insertions(+), 193 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart index d651a80cccf..22334938441 100644 --- a/packages/flutter/lib/src/cupertino/route.dart +++ b/packages/flutter/lib/src/cupertino/route.dart @@ -589,23 +589,39 @@ class _CupertinoBackGestureController { class _CupertinoEdgeShadowDecoration extends Decoration { const _CupertinoEdgeShadowDecoration({ this.edgeGradient }); - /// A Decoration with no decorating properties. + // An edge shadow decoration where the shadow is null. This is used + // for interpolating from no shadow. static const _CupertinoEdgeShadowDecoration none = const _CupertinoEdgeShadowDecoration(); - /// A gradient to draw to the left of the box being decorated. - /// Alignments are relative to the original box translated one box - /// width to the left. + // A gradient to draw to the left of the box being decorated. + // Alignments are relative to the original box translated one box + // width to the left. final LinearGradient edgeGradient; - /// Linearly interpolate between two edge shadow decorations decorations. - /// - /// See also [Decoration.lerp]. + // Linearly interpolate between two edge shadow decorations decorations. + // + // 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], such as + // an [AnimationController]. + // + // See also: + // + // * [Decoration.lerp]. static _CupertinoEdgeShadowDecoration lerp( _CupertinoEdgeShadowDecoration a, _CupertinoEdgeShadowDecoration b, - double t + double t, ) { + assert(t != null); if (a == null && b == null) return null; return new _CupertinoEdgeShadowDecoration( diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index bbc9056461a..38216230054 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -519,40 +519,53 @@ class ThemeData { /// Linearly interpolate between two themes. /// /// The arguments must not be null. - static ThemeData lerp(ThemeData begin, ThemeData end, double t) { - assert(begin != null); - assert(end != null); + /// + /// 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], such as + /// an [AnimationController]. + static ThemeData lerp(ThemeData a, ThemeData b, double t) { + assert(a != null); + assert(b != null); + assert(t != null); return new ThemeData.raw( - brightness: t < 0.5 ? begin.brightness : end.brightness, - primaryColor: Color.lerp(begin.primaryColor, end.primaryColor, t), - primaryColorBrightness: t < 0.5 ? begin.primaryColorBrightness : end.primaryColorBrightness, - canvasColor: Color.lerp(begin.canvasColor, end.canvasColor, t), - scaffoldBackgroundColor: Color.lerp(begin.scaffoldBackgroundColor, end.scaffoldBackgroundColor, t), - cardColor: Color.lerp(begin.cardColor, end.cardColor, t), - dividerColor: Color.lerp(begin.dividerColor, end.dividerColor, t), - highlightColor: Color.lerp(begin.highlightColor, end.highlightColor, t), - splashColor: Color.lerp(begin.splashColor, end.splashColor, t), - selectedRowColor: Color.lerp(begin.selectedRowColor, end.selectedRowColor, t), - unselectedWidgetColor: Color.lerp(begin.unselectedWidgetColor, end.unselectedWidgetColor, t), - disabledColor: Color.lerp(begin.disabledColor, end.disabledColor, t), - buttonColor: Color.lerp(begin.buttonColor, end.buttonColor, t), - secondaryHeaderColor: Color.lerp(begin.secondaryHeaderColor, end.secondaryHeaderColor, t), - textSelectionColor: Color.lerp(begin.textSelectionColor, end.textSelectionColor, t), - textSelectionHandleColor: Color.lerp(begin.textSelectionHandleColor, end.textSelectionHandleColor, t), - backgroundColor: Color.lerp(begin.backgroundColor, end.backgroundColor, t), - dialogBackgroundColor: Color.lerp(begin.dialogBackgroundColor, end.dialogBackgroundColor, t), - accentColor: Color.lerp(begin.accentColor, end.accentColor, t), - accentColorBrightness: t < 0.5 ? begin.accentColorBrightness : end.accentColorBrightness, - indicatorColor: Color.lerp(begin.indicatorColor, end.indicatorColor, t), - hintColor: Color.lerp(begin.hintColor, end.hintColor, t), - errorColor: Color.lerp(begin.errorColor, end.errorColor, t), - textTheme: TextTheme.lerp(begin.textTheme, end.textTheme, t), - primaryTextTheme: TextTheme.lerp(begin.primaryTextTheme, end.primaryTextTheme, t), - accentTextTheme: TextTheme.lerp(begin.accentTextTheme, end.accentTextTheme, t), - iconTheme: IconThemeData.lerp(begin.iconTheme, end.iconTheme, t), - primaryIconTheme: IconThemeData.lerp(begin.primaryIconTheme, end.primaryIconTheme, t), - accentIconTheme: IconThemeData.lerp(begin.accentIconTheme, end.accentIconTheme, t), - platform: t < 0.5 ? begin.platform : end.platform + brightness: t < 0.5 ? a.brightness : b.brightness, + primaryColor: Color.lerp(a.primaryColor, b.primaryColor, t), + primaryColorBrightness: t < 0.5 ? a.primaryColorBrightness : b.primaryColorBrightness, + canvasColor: Color.lerp(a.canvasColor, b.canvasColor, t), + scaffoldBackgroundColor: Color.lerp(a.scaffoldBackgroundColor, b.scaffoldBackgroundColor, t), + cardColor: Color.lerp(a.cardColor, b.cardColor, t), + dividerColor: Color.lerp(a.dividerColor, b.dividerColor, t), + highlightColor: Color.lerp(a.highlightColor, b.highlightColor, t), + splashColor: Color.lerp(a.splashColor, b.splashColor, t), + selectedRowColor: Color.lerp(a.selectedRowColor, b.selectedRowColor, t), + unselectedWidgetColor: Color.lerp(a.unselectedWidgetColor, b.unselectedWidgetColor, t), + disabledColor: Color.lerp(a.disabledColor, b.disabledColor, t), + buttonColor: Color.lerp(a.buttonColor, b.buttonColor, t), + secondaryHeaderColor: Color.lerp(a.secondaryHeaderColor, b.secondaryHeaderColor, t), + textSelectionColor: Color.lerp(a.textSelectionColor, b.textSelectionColor, t), + textSelectionHandleColor: Color.lerp(a.textSelectionHandleColor, b.textSelectionHandleColor, t), + backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), + dialogBackgroundColor: Color.lerp(a.dialogBackgroundColor, b.dialogBackgroundColor, t), + accentColor: Color.lerp(a.accentColor, b.accentColor, t), + accentColorBrightness: t < 0.5 ? a.accentColorBrightness : b.accentColorBrightness, + indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t), + hintColor: Color.lerp(a.hintColor, b.hintColor, t), + errorColor: Color.lerp(a.errorColor, b.errorColor, t), + textTheme: TextTheme.lerp(a.textTheme, b.textTheme, t), + primaryTextTheme: TextTheme.lerp(a.primaryTextTheme, b.primaryTextTheme, t), + accentTextTheme: TextTheme.lerp(a.accentTextTheme, b.accentTextTheme, t), + iconTheme: IconThemeData.lerp(a.iconTheme, b.iconTheme, t), + primaryIconTheme: IconThemeData.lerp(a.primaryIconTheme, b.primaryIconTheme, t), + accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t), + platform: t < 0.5 ? a.platform : b.platform, ); } diff --git a/packages/flutter/lib/src/material/typography.dart b/packages/flutter/lib/src/material/typography.dart index 0acc9b4bacb..2e131af2a9d 100644 --- a/packages/flutter/lib/src/material/typography.dart +++ b/packages/flutter/lib/src/material/typography.dart @@ -284,19 +284,36 @@ class TextTheme { } /// Linearly interpolate between two text themes. - static TextTheme lerp(TextTheme begin, TextTheme end, double t) { + /// + /// The arguments must not be null. + /// + /// 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], such as + /// an [AnimationController]. + static TextTheme lerp(TextTheme a, TextTheme b, double t) { + assert(a != null); + assert(b != null); + assert(t != null); return new TextTheme( - display4: TextStyle.lerp(begin.display4, end.display4, t), - display3: TextStyle.lerp(begin.display3, end.display3, t), - display2: TextStyle.lerp(begin.display2, end.display2, t), - display1: TextStyle.lerp(begin.display1, end.display1, t), - headline: TextStyle.lerp(begin.headline, end.headline, t), - title: TextStyle.lerp(begin.title, end.title, t), - subhead: TextStyle.lerp(begin.subhead, end.subhead, t), - body2: TextStyle.lerp(begin.body2, end.body2, t), - body1: TextStyle.lerp(begin.body1, end.body1, t), - caption: TextStyle.lerp(begin.caption, end.caption, t), - button: TextStyle.lerp(begin.button, end.button, t), + display4: TextStyle.lerp(a.display4, b.display4, t), + display3: TextStyle.lerp(a.display3, b.display3, t), + display2: TextStyle.lerp(a.display2, b.display2, t), + display1: TextStyle.lerp(a.display1, b.display1, t), + headline: TextStyle.lerp(a.headline, b.headline, t), + title: TextStyle.lerp(a.title, b.title, t), + subhead: TextStyle.lerp(a.subhead, b.subhead, t), + body2: TextStyle.lerp(a.body2, b.body2, t), + body1: TextStyle.lerp(a.body1, b.body1, t), + caption: TextStyle.lerp(a.caption, b.caption, t), + button: TextStyle.lerp(a.button, b.button, t), ); } diff --git a/packages/flutter/lib/src/painting/alignment.dart b/packages/flutter/lib/src/painting/alignment.dart index d21fa5d9f03..d1531829c8c 100644 --- a/packages/flutter/lib/src/painting/alignment.dart +++ b/packages/flutter/lib/src/painting/alignment.dart @@ -84,7 +84,20 @@ abstract class AlignmentGeometry { /// this is not reflected in the type system). Otherwise, an object /// representing a combination of both is returned. That object can be turned /// into a concrete [Alignment] using [resolve]. + /// + /// 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], such as + /// an [AnimationController]. static AlignmentGeometry lerp(AlignmentGeometry a, AlignmentGeometry b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -320,7 +333,20 @@ class Alignment extends AlignmentGeometry { /// Linearly interpolate between two [Alignment]s. /// /// If either is null, this function interpolates from [Alignment.center]. + /// + /// 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], such as + /// an [AnimationController]. static Alignment lerp(Alignment a, Alignment b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -498,7 +524,20 @@ class AlignmentDirectional extends AlignmentGeometry { /// Linearly interpolate between two [AlignmentDirectional]s. /// /// If either is null, this function interpolates from [AlignmentDirectional.center]. + /// + /// 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], such as + /// an [AnimationController]. static AlignmentDirectional lerp(AlignmentDirectional a, AlignmentDirectional b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) diff --git a/packages/flutter/lib/src/painting/border_radius.dart b/packages/flutter/lib/src/painting/border_radius.dart index 6439a5a6718..365610887b5 100644 --- a/packages/flutter/lib/src/painting/border_radius.dart +++ b/packages/flutter/lib/src/painting/border_radius.dart @@ -126,7 +126,20 @@ abstract class BorderRadiusGeometry { /// this is not reflected in the type system). Otherwise, an object /// representing a combination of both is returned. That object can be turned /// into a concrete [BorderRadius] using [resolve]. + /// + /// 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], such as + /// an [AnimationController]. static BorderRadiusGeometry lerp(BorderRadiusGeometry a, BorderRadiusGeometry b, double t) { + assert(t != null); if (a == null && b == null) return null; a ??= BorderRadius.zero; @@ -467,7 +480,20 @@ class BorderRadius extends BorderRadiusGeometry { /// Linearly interpolate between two [BorderRadius] objects. /// /// If either is null, this function interpolates from [BorderRadius.zero]. + /// + /// 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], such as + /// an [AnimationController]. static BorderRadius lerp(BorderRadius a, BorderRadius b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -681,7 +707,20 @@ class BorderRadiusDirectional extends BorderRadiusGeometry { /// Linearly interpolate between two [BorderRadiusDirectional] objects. /// /// If either is null, this function interpolates from [BorderRadiusDirectional.zero]. + /// + /// 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], such as + /// an [AnimationController]. static BorderRadiusDirectional lerp(BorderRadiusDirectional a, BorderRadiusDirectional b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) diff --git a/packages/flutter/lib/src/painting/borders.dart b/packages/flutter/lib/src/painting/borders.dart index d1741b1b06d..0bc4338da7a 100644 --- a/packages/flutter/lib/src/painting/borders.dart +++ b/packages/flutter/lib/src/painting/borders.dart @@ -130,11 +130,22 @@ class BorderSide { ); } - /// Creates a copy of this border but with the width scaled by the given factor. + /// Creates a copy of this border side description but with the width scaled + /// by the factor `t`. /// - /// Since a zero width is painted as a hairline width rather than no border at - /// all, the zero factor is special-cased to instead change the style no - /// [BorderStyle.none]. + /// The `t` argument represents the multiplicand, or the position on the + /// timeline for an interpolation from nothing to `this`, with 0.0 meaning + /// that the the object returned should be the nil variant of this object, 1.0 + /// meaning that no change should be applied, returning `this` (or something + /// equivalent to `this`), and other values meaning that the object should be + /// multiplied by `t`. Negative values are treated like zero. + /// + /// Since a zero width is normally painted as a hairline width rather than no + /// border at all, the zero factor is special-cased to instead change the + /// style no [BorderStyle.none]. + /// + /// Values for `t` are usually obtained from an [Animation], such as + /// an [AnimationController]. BorderSide scale(double t) { assert(t != null); return new BorderSide( @@ -186,6 +197,18 @@ class BorderSide { /// Linearly interpolate between two border sides. /// /// The arguments must not be null. + /// + /// 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], such as + /// an [AnimationController]. static BorderSide lerp(BorderSide a, BorderSide b, double t) { assert(a != null); assert(b != null); @@ -272,7 +295,7 @@ abstract class ShapeBorder { /// computing their [dimensions]. EdgeInsetsGeometry get dimensions; - /// Attempts to create a new object that represents the amalgamation of [this] + /// Attempts to create a new object that represents the amalgamation of `this` /// border and the `other` border. /// /// If the type of the other border isn't known, or the given instance cannot @@ -297,10 +320,32 @@ abstract class ShapeBorder { return add(other) ?? other.add(this, reversed: true) ?? new _CompoundBorder([other, this]); } - /// Creates a new border with the widths of this border multiplied by `t`. + /// Creates a copy of this border, scaled by the factor `t`. + /// + /// Typically this means scaling the width of the border's side, but it can + /// also include scaling other artifacts of the border, e.g. the border radius + /// of a [RoundedRectangleBorder]. + /// + /// The `t` argument represents the multiplicand, or the position on the + /// timeline for an interpolation from nothing to `this`, with 0.0 meaning + /// that the the object returned should be the nil variant of this object, 1.0 + /// meaning that no change should be applied, returning `this` (or something + /// equivalent to `this`), and other values meaning that the object should be + /// multiplied by `t`. Negative values are allowed but may be meaningless + /// (they correspond to extrapolating the interpolation from this object to + /// nothing, and going beyond nothing) + /// + /// Values for `t` are usually obtained from an [Animation], such as + /// an [AnimationController]. + /// + /// See also: + /// + /// * [BorderSide.scale], which most [ShapeBorder] subclasses defer to for + /// the actual computation. ShapeBorder scale(double t); - /// Linearly interpolates from `a` to [this]. + /// Linearly interpolates from another [ShapeBorder] (possibly of another + /// class) to `this`. /// /// When implementing this method in subclasses, return null if this class /// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo] @@ -309,6 +354,19 @@ abstract class ShapeBorder { /// The base class implementation handles the case of `a` being null by /// deferring to [scale]. /// + /// 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 `this` (or something equivalent to `this`), and values in + /// between meaning that the interpolation is at the relevant point on the + /// timeline between `a` and `this`. 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], such as + /// an [AnimationController]. + /// /// Instead of calling this directly, use [ShapeBorder.lerp]. @protected ShapeBorder lerpFrom(ShapeBorder a, double t) { @@ -317,7 +375,8 @@ abstract class ShapeBorder { return null; } - /// Linearly interpolates from [this] to `b`. + /// Linearly interpolates from `this` to another [ShapeBorder] (possibly of + /// another class). /// /// This is called if `b`'s [lerpTo] did not know how to handle this class. /// @@ -328,6 +387,18 @@ abstract class ShapeBorder { /// The base class implementation handles the case of `b` being null by /// deferring to [scale]. /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `this` (or something + /// equivalent to `this`), 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 `this` 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], such as + /// an [AnimationController]. + /// /// Instead of calling this directly, use [ShapeBorder.lerp]. @protected ShapeBorder lerpTo(ShapeBorder b, double t) { @@ -336,19 +407,32 @@ abstract class ShapeBorder { return null; } - /// Linearly interpolates from `begin` to `end`. + /// Linearly interpolates between two [ShapeBorder]s. /// - /// This defers to `end`'s [lerpTo] function if `end` is not null. If `end` is - /// null or if its [lerpTo] returns null, it uses `begin`'s [lerpFrom] - /// function instead. If both return null, it returns `begin` before `t=0.5` - /// and `end` after `t=0.5`. - static ShapeBorder lerp(ShapeBorder begin, ShapeBorder end, double t) { + /// This defers to `b`'s [lerpTo] function if `b` is not null. If `b` is + /// null or if its [lerpTo] returns null, it uses `a`'s [lerpFrom] + /// function instead. If both return null, it returns `a` before `t=0.5` + /// and `b` after `t=0.5`. + /// + /// 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], such as + /// an [AnimationController]. + static ShapeBorder lerp(ShapeBorder a, ShapeBorder b, double t) { + assert(t != null); ShapeBorder result; - if (end != null) - result = end.lerpFrom(begin, t); - if (result == null && begin != null) - result = begin.lerpTo(end, t); - return result ?? (t < 0.5 ? begin : end); + if (b != null) + result = b.lerpFrom(a, t); + if (result == null && a != null) + result = a.lerpTo(b, t); + return result ?? (t < 0.5 ? a : b); } /// Create a [Path] that describes the outer edge of the border. @@ -480,6 +564,7 @@ class _CompoundBorder extends ShapeBorder { } static _CompoundBorder lerp(ShapeBorder a, ShapeBorder b, double t) { + assert(t != null); assert(a is _CompoundBorder || b is _CompoundBorder); // Not really necessary, but all call sites currently intend this. final List aList = a is _CompoundBorder ? a.borders : [a]; final List bList = b is _CompoundBorder ? b.borders : [b]; diff --git a/packages/flutter/lib/src/painting/box_border.dart b/packages/flutter/lib/src/painting/box_border.dart index 9f470306523..54f4b7a0044 100644 --- a/packages/flutter/lib/src/painting/box_border.dart +++ b/packages/flutter/lib/src/painting/box_border.dart @@ -34,6 +34,8 @@ enum BoxShape { /// /// * [CircleBorder], the equivalent [ShapeBorder]. circle, + + // Don't add more, instead create a new ShapeBorder. } /// Base class for box borders that can paint as rectangle, circles, or rounded @@ -95,7 +97,20 @@ abstract class BoxBorder extends ShapeBorder { /// /// 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], such as + /// an [AnimationController]. static BoxBorder lerp(BoxBorder a, BoxBorder b, double t) { + assert(t != null); if ((a is Border || a == null) && (b is Border || b == null)) return Border.lerp(a, b, t); if ((a is BorderDirectional || a == null) && (b is BorderDirectional || b == null)) @@ -387,7 +402,6 @@ class Border extends BoxBorder { return null; } - /// Creates a new border with the widths of this border multiplied by `t`. @override Border scale(double t) { return new Border( @@ -398,13 +412,6 @@ class Border extends BoxBorder { ); } - /// Linearly interpolates from `a` to [this]. - /// - /// If `a` is null, this defers to [scale]. - /// - /// If `a` is also a [Border], this uses [Border.lerp]. - /// - /// Otherwise, it defers to [ShapeBorder.lerpFrom]. @override ShapeBorder lerpFrom(ShapeBorder a, double t) { if (a is Border) @@ -412,13 +419,6 @@ class Border extends BoxBorder { return super.lerpFrom(a, t); } - /// Linearly interpolates from [this] to `b`. - /// - /// If `b` is null, this defers to [scale]. - /// - /// If `b` is also a [Border], this uses [Border.lerp]. - /// - /// Otherwise, it defers to [ShapeBorder.lerpTo]. @override ShapeBorder lerpTo(ShapeBorder b, double t) { if (b is Border) @@ -430,7 +430,20 @@ class Border extends BoxBorder { /// /// If a border is null, it is treated as having four [BorderSide.none] /// borders. + /// + /// 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], such as + /// an [AnimationController]. static Border lerp(Border a, Border b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -441,7 +454,7 @@ class Border extends BoxBorder { top: BorderSide.lerp(a.top, b.top, t), right: BorderSide.lerp(a.right, b.right, t), bottom: BorderSide.lerp(a.bottom, b.bottom, t), - left: BorderSide.lerp(a.left, b.left, t) + left: BorderSide.lerp(a.left, b.left, t), ); } @@ -689,7 +702,6 @@ class BorderDirectional extends BoxBorder { return null; } - /// Creates a new border with the widths of this border multiplied by `t`. @override BorderDirectional scale(double t) { return new BorderDirectional( @@ -700,13 +712,6 @@ class BorderDirectional extends BoxBorder { ); } - /// Linearly interpolates from `a` to [this]. - /// - /// If `a` is null, this defers to [scale]. - /// - /// If `a` is also a [BorderDirectional], this uses [BorderDirectional.lerp]. - /// - /// Otherwise, it defers to [ShapeBorder.lerpFrom]. @override ShapeBorder lerpFrom(ShapeBorder a, double t) { if (a is BorderDirectional) @@ -714,13 +719,6 @@ class BorderDirectional extends BoxBorder { return super.lerpFrom(a, t); } - /// Linearly interpolates from [this] to `b`. - /// - /// If `b` is null, this defers to [scale]. - /// - /// If `b` is also a [BorderDirectional], this uses [BorderDirectional.lerp]. - /// - /// Otherwise, it defers to [ShapeBorder.lerpTo]. @override ShapeBorder lerpTo(ShapeBorder b, double t) { if (b is BorderDirectional) @@ -732,7 +730,20 @@ class BorderDirectional extends BoxBorder { /// /// If a border is null, it is treated as having four [BorderSide.none] /// borders. + /// + /// 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], such as + /// an [AnimationController]. static BorderDirectional lerp(BorderDirectional a, BorderDirectional b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -743,7 +754,7 @@ class BorderDirectional extends BoxBorder { top: BorderSide.lerp(a.top, b.top, t), end: BorderSide.lerp(a.end, b.end, t), bottom: BorderSide.lerp(a.bottom, b.bottom, t), - start: BorderSide.lerp(a.start, b.start, t) + start: BorderSide.lerp(a.start, b.start, t), ); } diff --git a/packages/flutter/lib/src/painting/box_decoration.dart b/packages/flutter/lib/src/painting/box_decoration.dart index 43c8a69bd7a..8a4f9d26883 100644 --- a/packages/flutter/lib/src/painting/box_decoration.dart +++ b/packages/flutter/lib/src/painting/box_decoration.dart @@ -200,6 +200,18 @@ class BoxDecoration extends Decoration { /// unmodified. Otherwise, the values are computed by interpolating the /// properties appropriately. /// + /// 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], such as + /// an [AnimationController]. + /// /// See also: /// /// * [Decoration.lerp], which can interpolate between any two types of @@ -208,6 +220,7 @@ class BoxDecoration extends Decoration { /// and which use [BoxDecoration.lerp] when interpolating two /// [BoxDecoration]s or a [BoxDecoration] to or from null. static BoxDecoration lerp(BoxDecoration a, BoxDecoration b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) diff --git a/packages/flutter/lib/src/painting/box_shadow.dart b/packages/flutter/lib/src/painting/box_shadow.dart index 28b8baaf9eb..2ef2407ec2a 100644 --- a/packages/flutter/lib/src/painting/box_shadow.dart +++ b/packages/flutter/lib/src/painting/box_shadow.dart @@ -70,7 +70,20 @@ class BoxShadow { /// If either box shadow is null, this function linearly interpolates from a /// a box shadow that matches the other box shadow in color but has a zero /// offset and a zero blurRadius. + /// + /// 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], such as + /// an [AnimationController]. static BoxShadow lerp(BoxShadow a, BoxShadow b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -81,25 +94,38 @@ class BoxShadow { color: Color.lerp(a.color, b.color, t), offset: Offset.lerp(a.offset, b.offset, t), blurRadius: ui.lerpDouble(a.blurRadius, b.blurRadius, t), - spreadRadius: ui.lerpDouble(a.spreadRadius, b.spreadRadius, t) + spreadRadius: ui.lerpDouble(a.spreadRadius, b.spreadRadius, t), ); } /// Linearly interpolate between two lists of box shadows. /// /// If the lists differ in length, excess items are lerped with null. + /// + /// 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], such as + /// an [AnimationController]. static List lerpList(List a, List b, double t) { + assert(t != null); if (a == null && b == null) return null; a ??= []; b ??= []; final List result = []; final int commonLength = math.min(a.length, b.length); - for (int i = 0; i < commonLength; ++i) + for (int i = 0; i < commonLength; i += 1) result.add(BoxShadow.lerp(a[i], b[i], t)); - for (int i = commonLength; i < a.length; ++i) + for (int i = commonLength; i < a.length; i += 1) result.add(a[i].scale(1.0 - t)); - for (int i = commonLength; i < b.length; ++i) + for (int i = commonLength; i < b.length; i += 1) result.add(b[i].scale(t)); return result; } diff --git a/packages/flutter/lib/src/painting/colors.dart b/packages/flutter/lib/src/painting/colors.dart index f1e0d1f2029..e0a8269e36f 100644 --- a/packages/flutter/lib/src/painting/colors.dart +++ b/packages/flutter/lib/src/painting/colors.dart @@ -136,9 +136,30 @@ class HSVColor { /// Linearly interpolate between two HSVColors. /// + /// The colors are interpolated by interpolating the [alpha], [hue], + /// [saturation], and [value] channels separately, which usually leads to a + /// more pleasing effect than [Color.lerp] (which interpolates the red, green, + /// and blue channels separately). + /// /// If either color is null, this function linearly interpolates from a - /// transparent instance of the other color. + /// transparent instance of the other color. This is usually preferable to + /// interpolating from [Colors.transparent] (`const Color(0x00000000)`) since + /// that will interpolate from a transparent red and cycle through the hues to + /// match the target color, regardless of what that color's hue is. + /// + /// 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], such as + /// an [AnimationController]. static HSVColor lerp(HSVColor a, HSVColor b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -149,7 +170,7 @@ class HSVColor { lerpDouble(a.alpha, b.alpha, t), lerpDouble(a.hue, b.hue, t), lerpDouble(a.saturation, b.saturation, t), - lerpDouble(a.value, b.value, t) + lerpDouble(a.value, b.value, t), ); } diff --git a/packages/flutter/lib/src/painting/decoration.dart b/packages/flutter/lib/src/painting/decoration.dart index c8b22ce39dd..e27252d58ff 100644 --- a/packages/flutter/lib/src/painting/decoration.dart +++ b/packages/flutter/lib/src/painting/decoration.dart @@ -61,7 +61,8 @@ abstract class Decoration extends Diagnosticable { /// Whether this decoration is complex enough to benefit from caching its painting. bool get isComplex => false; - /// Linearly interpolates from `a` to [this]. + /// Linearly interpolates from another [Decoration] (which may be of a + /// different class) to `this`. /// /// When implementing this method in subclasses, return null if this class /// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo] @@ -71,11 +72,25 @@ abstract class Decoration extends Diagnosticable { /// method uses this as a fallback when two classes can't interpolate between /// each other. /// + /// 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 `this` (or something equivalent to `this`), and values in + /// between meaning that the interpolation is at the relevant point on the + /// timeline between `a` and `this`. 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], such as + /// an [AnimationController]. + /// /// Instead of calling this directly, use [Decoration.lerp]. @protected Decoration lerpFrom(Decoration a, double t) => null; - /// Linearly interpolates from [this] to `b`. + /// Linearly interpolates from `this` to another [Decoration] (which may be of + /// a different class). /// /// This is called if `b`'s [lerpTo] did not know how to handle this class. /// @@ -87,32 +102,54 @@ abstract class Decoration extends Diagnosticable { /// method uses this as a fallback when two classes can't interpolate between /// each other. /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `this` (or something + /// equivalent to `this`), 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 `this` 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], such as + /// an [AnimationController]. + /// /// Instead of calling this directly, use [Decoration.lerp]. @protected Decoration lerpTo(Decoration b, double t) => null; - /// Linearly interpolates from `begin` to `end`. + /// Linearly interpolates between two [Decoration]s. /// - /// This attempts to use [lerpFrom] and [lerpTo] on `end` and `begin` + /// This attempts to use [lerpFrom] and [lerpTo] on `b` and `a` /// respectively to find a solution. If the two values can't directly be /// interpolated, then the interpolation is done via null (at `t == 0.5`). /// - /// If the values aren't null, then for `t == 0.0` and `t == 1.0` the values - /// `begin` and `end` are return verbatim. - static Decoration lerp(Decoration begin, Decoration end, double t) { - if (begin == null && end == null) + /// 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], such as + /// an [AnimationController]. + static Decoration lerp(Decoration a, Decoration b, double t) { + assert(t != null); + if (a == null && b == null) return null; - if (begin == null) - return end.lerpFrom(null, t) ?? end; - if (end == null) - return begin.lerpTo(null, t) ?? begin; + if (a == null) + return b.lerpFrom(null, t) ?? b; + if (b == null) + return a.lerpTo(null, t) ?? a; if (t == 0.0) - return begin; + return a; if (t == 1.0) - return end; - return end.lerpFrom(begin, t) - ?? begin.lerpTo(end, t) - ?? (t < 0.5 ? begin.lerpTo(null, t * 2.0) : end.lerpFrom(null, (t - 0.5) * 2.0)); + return b; + return b.lerpFrom(a, t) + ?? a.lerpTo(b, t) + ?? (t < 0.5 ? (a.lerpTo(null, t * 2.0) ?? a) : (b.lerpFrom(null, (t - 0.5) * 2.0) ?? b)); } /// Tests whether the given point, on a rectangle of a given size, diff --git a/packages/flutter/lib/src/painting/edge_insets.dart b/packages/flutter/lib/src/painting/edge_insets.dart index 7da83633f7d..652dc064326 100644 --- a/packages/flutter/lib/src/painting/edge_insets.dart +++ b/packages/flutter/lib/src/painting/edge_insets.dart @@ -189,7 +189,20 @@ abstract class EdgeInsetsGeometry { /// this is not reflected in the type system). Otherwise, an object /// representing a combination of both is returned. That object can be turned /// into a concrete [EdgeInsets] using [resolve]. + /// + /// 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], such as + /// an [AnimationController]. static EdgeInsetsGeometry lerp(EdgeInsetsGeometry a, EdgeInsetsGeometry b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -206,7 +219,7 @@ abstract class EdgeInsetsGeometry { ui.lerpDouble(a._start, b._start, t), ui.lerpDouble(a._end, b._end, t), ui.lerpDouble(a._top, b._top, t), - ui.lerpDouble(a._bottom, b._bottom, t) + ui.lerpDouble(a._bottom, b._bottom, t), ); } @@ -542,7 +555,20 @@ class EdgeInsets extends EdgeInsetsGeometry { /// Linearly interpolate between two [EdgeInsets]. /// /// If either is null, this function interpolates from [EdgeInsets.zero]. + /// + /// 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], such as + /// an [AnimationController]. static EdgeInsets lerp(EdgeInsets a, EdgeInsets b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -763,7 +789,20 @@ class EdgeInsetsDirectional extends EdgeInsetsGeometry { /// To interpolate between two [EdgeInsetsGeometry] objects of arbitrary type /// (either [EdgeInsets] or [EdgeInsetsDirectional]), consider the /// [EdgeInsetsGeometry.lerp] static method. + /// + /// 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], such as + /// an [AnimationController]. static EdgeInsetsDirectional lerp(EdgeInsetsDirectional a, EdgeInsetsDirectional b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) diff --git a/packages/flutter/lib/src/painting/flutter_logo.dart b/packages/flutter/lib/src/painting/flutter_logo.dart index 9675c4185b2..29e0b5511e8 100644 --- a/packages/flutter/lib/src/painting/flutter_logo.dart +++ b/packages/flutter/lib/src/painting/flutter_logo.dart @@ -125,13 +125,25 @@ class FlutterLogoDecoration extends Decoration { /// /// If both values are null, this returns null. Otherwise, it returns a /// non-null value. If one of the values is null, then the result is obtained - /// by scaling the other value's opacity and [margin]. If neither value is - /// null and `t == 0.0`, then `a` is returned unmodified; if `t == 1.0` then - /// `b` is returned unmodified. Otherwise, the values are computed by - /// interpolating the properties appropriately. + /// by scaling the other value's opacity and [margin]. /// - /// See also [Decoration.lerp]. + /// 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], such as + /// an [AnimationController]. + /// + /// See also: + /// + /// * [Decoration.lerp], which interpolates between arbitrary decorations. static FlutterLogoDecoration lerp(FlutterLogoDecoration a, FlutterLogoDecoration b, double t) { + assert(t != null); assert(a == null || a.debugAssertIsValid()); assert(b == null || b.debugAssertIsValid()); if (a == null && b == null) diff --git a/packages/flutter/lib/src/painting/fractional_offset.dart b/packages/flutter/lib/src/painting/fractional_offset.dart index 3a97436c5d8..0c023fa2a83 100644 --- a/packages/flutter/lib/src/painting/fractional_offset.dart +++ b/packages/flutter/lib/src/painting/fractional_offset.dart @@ -177,7 +177,20 @@ class FractionalOffset extends Alignment { /// Linearly interpolate between two [FractionalOffset]s. /// /// If either is null, this function interpolates from [FractionalOffset.center]. + /// + /// 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], such as + /// an [AnimationController]. static FractionalOffset lerp(FractionalOffset a, FractionalOffset b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index d10edd86c2b..2699ca4c582 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -67,7 +67,7 @@ abstract class Gradient { /// Typically this is the same as interpolating from null (with [lerp]). Gradient scale(double factor); - /// Linearly interpolates from `a` to [this]. + /// Linearly interpolates from another [Gradient] to `this`. /// /// When implementing this method in subclasses, return null if this class /// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo] @@ -76,6 +76,19 @@ abstract class Gradient { /// If `a` is null, this must not return null. The base class implements this /// by deferring to [scale]. /// + /// 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 `this` (or something equivalent to `this`), and values in + /// between meaning that the interpolation is at the relevant point on the + /// timeline between `a` and `this`. 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], such as + /// an [AnimationController]. + /// /// Instead of calling this directly, use [Gradient.lerp]. @protected Gradient lerpFrom(Gradient a, double t) { @@ -84,7 +97,7 @@ abstract class Gradient { return null; } - /// Linearly interpolates from [this] to `b`. + /// Linearly interpolates from `this` to another [Gradient]. /// /// This is called if `b`'s [lerpTo] did not know how to handle this class. /// @@ -95,6 +108,18 @@ abstract class Gradient { /// If `b` is null, this must not return null. The base class implements this /// by deferring to [scale]. /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `this` (or something + /// equivalent to `this`), 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 `this` 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], such as + /// an [AnimationController]. + /// /// Instead of calling this directly, use [Gradient.lerp]. @protected Gradient lerpTo(Gradient b, double t) { @@ -103,24 +128,37 @@ abstract class Gradient { return null; } - /// Linearly interpolates from `begin` to `end`. + /// Linearly interpolates between two [Gradient]s. /// - /// This defers to `end`'s [lerpTo] function if `end` is not null. If `end` is - /// null or if its [lerpTo] returns null, it uses `begin`'s [lerpFrom] - /// function instead. If both return null, it returns `begin` before `t=0.5` - /// and `end` after `t=0.5`. - static Gradient lerp(Gradient begin, Gradient end, double t) { + /// This defers to `b`'s [lerpTo] function if `b` is not null. If `b` is + /// null or if its [lerpTo] returns null, it uses `a`'s [lerpFrom] + /// function instead. If both return null, it returns `a` before `t == 0.5` + /// and `b` after `t == 0.5`. + /// + /// 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], such as + /// an [AnimationController]. + static Gradient lerp(Gradient a, Gradient b, double t) { + assert(t != null); Gradient result; - if (end != null) - result = end.lerpFrom(begin, t); // if begin is null, this must return non-null - if (result == null && begin != null) - result = begin.lerpTo(end, t); // if end is null, this must return non-null + if (b != null) + result = b.lerpFrom(a, t); // if a is null, this must return non-null + if (result == null && a != null) + result = a.lerpTo(b, t); // if b is null, this must return non-null if (result != null) return result; - if (begin == null && end == null) + if (a == null && b == null) return null; - assert(begin != null && end != null); - return t < 0.5 ? begin.scale(1.0 - (t * 2.0)) : end.scale((t - 0.5) * 2.0); + assert(a != null && b != null); + return t < 0.5 ? a.scale(1.0 - (t * 2.0)) : b.scale((t - 0.5) * 2.0); } } @@ -298,7 +336,20 @@ class LinearGradient extends Gradient { /// [tileMode] and with the same [colors] but transparent (using [scale]). /// /// If neither gradient is null, they must have the same number of [colors]. + /// + /// 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], such as + /// an [AnimationController]. static LinearGradient lerp(LinearGradient a, LinearGradient b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -532,7 +583,20 @@ class RadialGradient extends Gradient { /// [tileMode] and with the same [colors] but transparent (using [scale]). /// /// If neither gradient is null, they must have the same number of [colors]. + /// + /// 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], such as + /// an [AnimationController]. static RadialGradient lerp(RadialGradient a, RadialGradient b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) diff --git a/packages/flutter/lib/src/painting/rounded_rectangle_border.dart b/packages/flutter/lib/src/painting/rounded_rectangle_border.dart index 03213075b82..a01ab02c3ce 100644 --- a/packages/flutter/lib/src/painting/rounded_rectangle_border.dart +++ b/packages/flutter/lib/src/painting/rounded_rectangle_border.dart @@ -55,6 +55,7 @@ class RoundedRectangleBorder extends ShapeBorder { @override ShapeBorder lerpFrom(ShapeBorder a, double t) { + assert(t != null); if (a is RoundedRectangleBorder) { return new RoundedRectangleBorder( side: BorderSide.lerp(a.side, side, t), @@ -73,6 +74,7 @@ class RoundedRectangleBorder extends ShapeBorder { @override ShapeBorder lerpTo(ShapeBorder b, double t) { + assert(t != null); if (b is RoundedRectangleBorder) { return new RoundedRectangleBorder( side: BorderSide.lerp(side, b.side, t), @@ -169,6 +171,7 @@ class _RoundedRectangleToCircleBorder extends ShapeBorder { @override ShapeBorder lerpFrom(ShapeBorder a, double t) { + assert(t != null); if (a is RoundedRectangleBorder) { return new _RoundedRectangleToCircleBorder( side: BorderSide.lerp(a.side, side, t), diff --git a/packages/flutter/lib/src/painting/shape_decoration.dart b/packages/flutter/lib/src/painting/shape_decoration.dart index f48c4eaca01..7aaff498ef2 100644 --- a/packages/flutter/lib/src/painting/shape_decoration.dart +++ b/packages/flutter/lib/src/painting/shape_decoration.dart @@ -186,13 +186,22 @@ class ShapeDecoration extends Decoration { /// Interpolates each parameter of the decoration separately. /// /// If both values are null, this returns null. Otherwise, it returns a - /// non-null value. If neither value is null and `t == 0.0`, then `a` is - /// returned unmodified; if `t == 1.0` then `b` is returned unmodified. - /// Otherwise, the values are computed by interpolating the properties - /// appropriately, treating a null argument like a [ShapeDecoration] whose + /// non-null value, with null arguments treated like a [ShapeDecoration] whose /// fields are all null (including the [shape], which cannot normally be /// null). /// + /// 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], such as + /// an [AnimationController]. + /// /// See also: /// /// * [Decoration.lerp], which can interpolate between any two types of @@ -201,6 +210,7 @@ class ShapeDecoration extends Decoration { /// and which use [ShapeDecoration.lerp] when interpolating two /// [ShapeDecoration]s or a [ShapeDecoration] to or from null. static ShapeDecoration lerp(ShapeDecoration a, ShapeDecoration b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a != null && b != null) { diff --git a/packages/flutter/lib/src/painting/stadium_border.dart b/packages/flutter/lib/src/painting/stadium_border.dart index 0223e1f341b..c4422a9fe3d 100644 --- a/packages/flutter/lib/src/painting/stadium_border.dart +++ b/packages/flutter/lib/src/painting/stadium_border.dart @@ -41,6 +41,7 @@ class StadiumBorder extends ShapeBorder { @override ShapeBorder lerpFrom(ShapeBorder a, double t) { + assert(t != null); if (a is StadiumBorder) return new StadiumBorder(side: BorderSide.lerp(a.side, side, t)); if (a is CircleBorder) { @@ -61,6 +62,7 @@ class StadiumBorder extends ShapeBorder { @override ShapeBorder lerpTo(ShapeBorder b, double t) { + assert(t != null); if (b is StadiumBorder) return new StadiumBorder(side: BorderSide.lerp(side, b.side, t)); if (b is CircleBorder) { @@ -151,6 +153,7 @@ class _StadiumToCircleBorder extends ShapeBorder { @override ShapeBorder lerpFrom(ShapeBorder a, double t) { + assert(t != null); if (a is StadiumBorder) { return new _StadiumToCircleBorder( side: BorderSide.lerp(a.side, side, t), @@ -174,6 +177,7 @@ class _StadiumToCircleBorder extends ShapeBorder { @override ShapeBorder lerpTo(ShapeBorder b, double t) { + assert(t != null); if (b is StadiumBorder) { return new _StadiumToCircleBorder( side: BorderSide.lerp(side, b.side, t), @@ -303,6 +307,7 @@ class _StadiumToRoundedRectangleBorder extends ShapeBorder { @override ShapeBorder lerpFrom(ShapeBorder a, double t) { + assert(t != null); if (a is StadiumBorder) { return new _StadiumToRoundedRectangleBorder( side: BorderSide.lerp(a.side, side, t), @@ -329,6 +334,7 @@ class _StadiumToRoundedRectangleBorder extends ShapeBorder { @override ShapeBorder lerpTo(ShapeBorder b, double t) { + assert(t != null); if (b is StadiumBorder) { return new _StadiumToRoundedRectangleBorder( side: BorderSide.lerp(side, b.side, t), diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index c39ffd1dac0..251ecd64098 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -338,7 +338,7 @@ class TextStyle extends Diagnosticable { String newDebugLabel; assert(() { if (this.debugLabel != null) - newDebugLabel = debugLabel ?? 'copy of ${this.debugLabel}'; + newDebugLabel = debugLabel ?? '(${this.debugLabel}).copyWith'; return true; }()); return new TextStyle( @@ -414,7 +414,7 @@ class TextStyle extends Diagnosticable { String modifiedDebugLabel; assert(() { if (debugLabel != null) - modifiedDebugLabel = 'modified $debugLabel'; + modifiedDebugLabel = '($debugLabel).apply'; return true; }()); @@ -459,7 +459,7 @@ class TextStyle extends Diagnosticable { String mergedDebugLabel; assert(() { if (other.debugLabel != null || debugLabel != null) - mergedDebugLabel = '${other.debugLabel ?? _kDefaultDebugLabel} < ${debugLabel ?? _kDefaultDebugLabel}'; + mergedDebugLabel = '(${debugLabel ?? _kDefaultDebugLabel}).merge(${other.debugLabel ?? _kDefaultDebugLabel})'; return true; }()); @@ -483,29 +483,44 @@ class TextStyle extends Diagnosticable { /// Interpolate between two text styles. /// /// This will not work well if the styles don't set the same fields. - static TextStyle lerp(TextStyle begin, TextStyle end, double t) { - assert(begin.inherit == end.inherit); + /// + /// 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], such as + /// an [AnimationController]. + static TextStyle lerp(TextStyle a, TextStyle b, double t) { + assert(a != null); + assert(b != null); + assert(t != null); + assert(a.inherit == b.inherit); String lerpDebugLabel; assert(() { - lerpDebugLabel = 'lerp(${begin.debugLabel ?? _kDefaultDebugLabel}, ${end.debugLabel ?? _kDefaultDebugLabel})'; + lerpDebugLabel = 'lerp(${a.debugLabel ?? _kDefaultDebugLabel} ⎯${t.toStringAsFixed(1)}→ ${b.debugLabel ?? _kDefaultDebugLabel})'; return true; }()); return new TextStyle( - inherit: end.inherit, - color: Color.lerp(begin.color, end.color, t), - fontFamily: t < 0.5 ? begin.fontFamily : end.fontFamily, - fontSize: ui.lerpDouble(begin.fontSize ?? end.fontSize, end.fontSize ?? begin.fontSize, t), - fontWeight: FontWeight.lerp(begin.fontWeight, end.fontWeight, t), - fontStyle: t < 0.5 ? begin.fontStyle : end.fontStyle, - letterSpacing: ui.lerpDouble(begin.letterSpacing ?? end.letterSpacing, end.letterSpacing ?? begin.letterSpacing, t), - wordSpacing: ui.lerpDouble(begin.wordSpacing ?? end.wordSpacing, end.wordSpacing ?? begin.wordSpacing, t), - textBaseline: t < 0.5 ? begin.textBaseline : end.textBaseline, - height: ui.lerpDouble(begin.height ?? end.height, end.height ?? begin.height, t), - decoration: t < 0.5 ? begin.decoration : end.decoration, - decorationColor: Color.lerp(begin.decorationColor, end.decorationColor, t), - decorationStyle: t < 0.5 ? begin.decorationStyle : end.decorationStyle, + inherit: b.inherit, + color: Color.lerp(a.color, b.color, t), + fontFamily: t < 0.5 ? a.fontFamily : b.fontFamily, + 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), + decoration: t < 0.5 ? a.decoration : b.decoration, + decorationColor: Color.lerp(a.decorationColor, b.decorationColor, t), + decorationStyle: t < 0.5 ? a.decorationStyle : b.decorationStyle, debugLabel: lerpDebugLabel, ); } diff --git a/packages/flutter/lib/src/rendering/box.dart b/packages/flutter/lib/src/rendering/box.dart index b87cc89b81a..211d8cd329b 100644 --- a/packages/flutter/lib/src/rendering/box.dart +++ b/packages/flutter/lib/src/rendering/box.dart @@ -402,7 +402,20 @@ class BoxConstraints extends Constraints { /// /// If either is null, this function interpolates from a [BoxConstraints] /// object whose fields are all set to 0.0. + /// + /// 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], such as + /// an [AnimationController]. static BoxConstraints lerp(BoxConstraints a, BoxConstraints b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) diff --git a/packages/flutter/lib/src/rendering/stack.dart b/packages/flutter/lib/src/rendering/stack.dart index bcd6646358d..a7798b74734 100644 --- a/packages/flutter/lib/src/rendering/stack.dart +++ b/packages/flutter/lib/src/rendering/stack.dart @@ -124,7 +124,20 @@ class RelativeRect { /// Linearly interpolate between two RelativeRects. /// /// If either rect is null, this function interpolates from [RelativeRect.fill]. + /// + /// 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], such as + /// an [AnimationController]. static RelativeRect lerp(RelativeRect a, RelativeRect b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -137,7 +150,7 @@ class RelativeRect { lerpDouble(a.left, b.left, t), lerpDouble(a.top, b.top, t), lerpDouble(a.right, b.right, t), - lerpDouble(a.bottom, b.bottom, t) + lerpDouble(a.bottom, b.bottom, t), ); } diff --git a/packages/flutter/lib/src/rendering/table_border.dart b/packages/flutter/lib/src/rendering/table_border.dart index fdf13e14686..8f9c0087efd 100644 --- a/packages/flutter/lib/src/rendering/table_border.dart +++ b/packages/flutter/lib/src/rendering/table_border.dart @@ -116,15 +116,29 @@ class TableBorder { return true; } - /// Creates a new border with the widths of this border multiplied by `t`. + /// Creates a copy of this border but with the widths scaled by the factor `t`. + /// + /// The `t` argument represents the multiplicand, or the position on the + /// timeline for an interpolation from nothing to `this`, with 0.0 meaning + /// that the the object returned should be the nil variant of this object, 1.0 + /// meaning that no change should be applied, returning `this` (or something + /// equivalent to `this`), and other values meaning that the object should be + /// multiplied by `t`. Negative values are treated like zero. + /// + /// Values for `t` are usually obtained from an [Animation], such as + /// an [AnimationController]. + /// + /// See also: + /// + /// * [BorderSide.scale], which is used to implement this method. TableBorder scale(double t) { return new TableBorder( - top: top.copyWith(width: t * top.width), - right: right.copyWith(width: t * right.width), - bottom: bottom.copyWith(width: t * bottom.width), - left: left.copyWith(width: t * left.width), - horizontalInside: horizontalInside.copyWith(width: t * horizontalInside.width), - verticalInside: verticalInside.copyWith(width: t * verticalInside.width) + top: top.scale(t), + right: right.scale(t), + bottom: bottom.scale(t), + left: left.scale(t), + horizontalInside: horizontalInside.scale(t), + verticalInside: verticalInside.scale(t), ); } @@ -132,7 +146,20 @@ class TableBorder { /// /// If a border is null, it is treated as having only [BorderSide.none] /// borders. + /// + /// 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], such as + /// an [AnimationController]. static TableBorder lerp(TableBorder a, TableBorder b, double t) { + assert(t != null); if (a == null && b == null) return null; if (a == null) @@ -145,7 +172,7 @@ class TableBorder { bottom: BorderSide.lerp(a.bottom, b.bottom, t), left: BorderSide.lerp(a.left, b.left, t), horizontalInside: BorderSide.lerp(a.horizontalInside, b.horizontalInside, t), - verticalInside: BorderSide.lerp(a.verticalInside, b.verticalInside, t) + verticalInside: BorderSide.lerp(a.verticalInside, b.verticalInside, t), ); } diff --git a/packages/flutter/lib/src/widgets/icon_theme_data.dart b/packages/flutter/lib/src/widgets/icon_theme_data.dart index 06a9847f5a5..0dc5f41d050 100644 --- a/packages/flutter/lib/src/widgets/icon_theme_data.dart +++ b/packages/flutter/lib/src/widgets/icon_theme_data.dart @@ -65,11 +65,24 @@ class IconThemeData { final double size; /// Linearly interpolate between two icon theme data objects. - static IconThemeData lerp(IconThemeData begin, IconThemeData end, double t) { + /// + /// 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], such as + /// an [AnimationController]. + static IconThemeData lerp(IconThemeData a, IconThemeData b, double t) { + assert(t != null); return new IconThemeData( - color: Color.lerp(begin.color, end.color, t), - opacity: ui.lerpDouble(begin.opacity, end.opacity, t), - size: ui.lerpDouble(begin.size, end.size, t) + color: Color.lerp(a.color, b.color, t), + opacity: ui.lerpDouble(a.opacity, b.opacity, t), + size: ui.lerpDouble(a.size, b.size, t), ); } diff --git a/packages/flutter/test/material/theme_test.dart b/packages/flutter/test/material/theme_test.dart index 926f9017dd1..fbfa0af0c73 100644 --- a/packages/flutter/test/material/theme_test.dart +++ b/packages/flutter/test/material/theme_test.dart @@ -417,7 +417,7 @@ void main() { } } - expect(theme.textTheme.display4.debugLabel, 'blackMountainView display4 < englishLike display4'); + expect(theme.textTheme.display4.debugLabel, '(englishLike display4).merge(blackMountainView display4)'); }); } diff --git a/packages/flutter/test/painting/text_style_test.dart b/packages/flutter/test/painting/text_style_test.dart index fd2f607ce79..f6c1ff9d5e7 100644 --- a/packages/flutter/test/painting/text_style_test.dart +++ b/packages/flutter/test/painting/text_style_test.dart @@ -156,11 +156,11 @@ void main() { expect(foo.debugLabel, 'foo'); expect(foo.toString(), 'TextStyle(debugLabel: foo, inherit: true, size: 1.0)'); - expect(foo.merge(bar).debugLabel, 'bar < foo'); - expect(foo.merge(bar).merge(baz).debugLabel, 'baz < bar < foo'); - expect(foo.copyWith().debugLabel, 'copy of foo'); - expect(foo.apply().debugLabel, 'modified foo'); - expect(TextStyle.lerp(foo, bar, 0.5).debugLabel, 'lerp(foo, bar)'); - expect(TextStyle.lerp(foo.merge(bar), baz, 0.5).copyWith().debugLabel, 'copy of lerp(bar < foo, baz)'); + expect(foo.merge(bar).debugLabel, '(foo).merge(bar)'); + expect(foo.merge(bar).merge(baz).debugLabel, '((foo).merge(bar)).merge(baz)'); + expect(foo.copyWith().debugLabel, '(foo).copyWith'); + expect(foo.apply().debugLabel, '(foo).apply'); + expect(TextStyle.lerp(foo, bar, 0.5).debugLabel, 'lerp(foo ⎯0.5→ bar)'); + expect(TextStyle.lerp(foo.merge(bar), baz, 0.51).copyWith().debugLabel, '(lerp((foo).merge(bar) ⎯0.5→ baz)).copyWith'); }); } diff --git a/packages/flutter/test/rendering/table_border_test.dart b/packages/flutter/test/rendering/table_border_test.dart index 6e9d7170d0c..4357c1279e3 100644 --- a/packages/flutter/test/rendering/table_border_test.dart +++ b/packages/flutter/test/rendering/table_border_test.dart @@ -57,8 +57,8 @@ void main() { expect(border3.dimensions, const EdgeInsets.symmetric(horizontal: 1.0, vertical: 1.0)); expect(border3.isUniform, isFalse); expect(border3.scale(0.0), new TableBorder.symmetric( - inside: const BorderSide(width: 0.0), - outside: const BorderSide(width: 0.0, color: const Color(0xFFFF0000)), + inside: const BorderSide(width: 0.0, style: BorderStyle.none), + outside: const BorderSide(width: 0.0, color: const Color(0xFFFF0000), style: BorderStyle.none), )); });