Commit 73c9ebab authored by Ian Hickson's avatar Ian Hickson

Reimplement the theme transition animation by actually animating the Theme.

As part of this:
 - A lot of classes got new lerp functions, including e.g. TextStyle.
 - Theme's constructor story got overhauled. You can now configure
   everything if you really want to, and we're better about defaults.
 - Material no longer automatically animates its background color.
   (It still does for its shadow.)
 - Tabs try to get the indicator color from the theme.
 - The fields in ThemeData got reordered for sanity.
 - Theme.== and Theme.hashCode got fixed.
 - Typography got a bit of a spring cleaning.

Fixes #613.
parent 098249f7
...@@ -8,6 +8,12 @@ class IconThemeData { ...@@ -8,6 +8,12 @@ class IconThemeData {
const IconThemeData({ this.color }); const IconThemeData({ this.color });
final IconThemeColor color; final IconThemeColor color;
static IconThemeData lerp(IconThemeData begin, IconThemeData end, double t) {
return new IconThemeData(
color: t < 0.5 ? begin.color : end.color
);
}
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
if (other is! IconThemeData) if (other is! IconThemeData)
return false; return false;
......
...@@ -160,12 +160,18 @@ class _MaterialState extends State<Material> { ...@@ -160,12 +160,18 @@ class _MaterialState extends State<Material> {
curve: Curves.ease, curve: Curves.ease,
duration: kThemeChangeDuration, duration: kThemeChangeDuration,
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: backgroundColor,
borderRadius: kMaterialEdges[config.type], borderRadius: kMaterialEdges[config.type],
boxShadow: config.elevation == 0 ? null : elevationToShadow[config.elevation], boxShadow: config.elevation == 0 ? null : elevationToShadow[config.elevation],
shape: config.type == MaterialType.circle ? BoxShape.circle : BoxShape.rectangle shape: config.type == MaterialType.circle ? BoxShape.circle : BoxShape.rectangle
), ),
child: contents child: new Container(
decoration: new BoxDecoration(
borderRadius: kMaterialEdges[config.type],
backgroundColor: backgroundColor,
shape: config.type == MaterialType.circle ? BoxShape.circle : BoxShape.rectangle
),
child: contents
)
); );
} }
return contents; return contents;
......
...@@ -141,8 +141,9 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver { ...@@ -141,8 +141,9 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
data: new MediaQueryData(size: _size), data: new MediaQueryData(size: _size),
child: new LocaleQuery( child: new LocaleQuery(
data: _localeData, data: _localeData,
child: new Theme( child: new AnimatedTheme(
data: theme, data: theme,
duration: kThemeAnimationDuration,
child: new DefaultTextStyle( child: new DefaultTextStyle(
style: _errorTextStyle, style: _errorTextStyle,
child: new DefaultAssetBundle( child: new DefaultAssetBundle(
......
...@@ -717,9 +717,17 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect ...@@ -717,9 +717,17 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
ThemeData themeData = Theme.of(context); ThemeData themeData = Theme.of(context);
Color backgroundColor = Material.of(context).color; Color backgroundColor = Material.of(context).color;
Color indicatorColor = themeData.accentColor; Color indicatorColor = themeData.indicatorColor;
if (indicatorColor == backgroundColor) if (indicatorColor == backgroundColor) {
// ThemeData tries to avoid this by having indicatorColor avoid being the
// primaryColor. However, it's possible that the tab strip is on a
// Material that isn't the primaryColor. In that case, if the indicator
// color ends up clashing, then this overrides it. When that happens,
// automatic transitions of the theme will likely look ugly as the
// indicator color suddenly snaps to white at one end, but it's not clear
// how to avoid that any further.
indicatorColor = Colors.white; indicatorColor = Colors.white;
}
TextStyle textStyle = themeData.primaryTextTheme.body1; TextStyle textStyle = themeData.primaryTextTheme.body1;
IconThemeData iconTheme = themeData.primaryIconTheme; IconThemeData iconTheme = themeData.primaryIconTheme;
......
...@@ -2,12 +2,15 @@ ...@@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/animation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'theme_data.dart'; import 'theme_data.dart';
export 'theme_data.dart' show ThemeData, ThemeBrightness; export 'theme_data.dart' show ThemeData, ThemeBrightness;
const kThemeAnimationDuration = const Duration(milliseconds: 200);
class Theme extends InheritedWidget { class Theme extends InheritedWidget {
Theme({ Theme({
Key key, Key key,
...@@ -37,3 +40,55 @@ class Theme extends InheritedWidget { ...@@ -37,3 +40,55 @@ class Theme extends InheritedWidget {
description.add('$data'); description.add('$data');
} }
} }
/// An animated value that interpolates [BoxConstraint]s.
class AnimatedThemeDataValue extends AnimatedValue<ThemeData> {
AnimatedThemeDataValue(ThemeData begin, { ThemeData end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
ThemeData lerp(double t) => ThemeData.lerp(begin, end, t);
}
/// Animated version of [Theme] which automatically transitions the colours,
/// etc, over a given duration whenever the given theme changes.
class AnimatedTheme extends AnimatedWidgetBase {
AnimatedTheme({
Key key,
this.data,
Curve curve: Curves.linear,
Duration duration,
this.child
}) : super(key: key, curve: curve, duration: duration) {
assert(child != null);
assert(data != null);
}
final ThemeData data;
final Widget child;
_AnimatedThemeState createState() => new _AnimatedThemeState();
}
class _AnimatedThemeState extends AnimatedWidgetBaseState<AnimatedTheme> {
AnimatedThemeDataValue _data;
void forEachVariable(VariableVisitor visitor) {
// TODO(ianh): Use constructor tear-offs when it becomes possible
_data = visitor(_data, config.data, (dynamic value) => new AnimatedThemeDataValue(value));
assert(_data != null);
}
Widget build(BuildContext context) {
return new Theme(
child: config.child,
data: _data.value
);
}
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
if (_data != null)
description.add('$_data');
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show Color, hashValues; import 'dart:ui' show Color, hashValues, hashList, lerpDouble;
import 'colors.dart'; import 'colors.dart';
import 'icon_theme_data.dart'; import 'icon_theme_data.dart';
...@@ -29,41 +29,109 @@ const Color _kDarkThemeSplashColor = const Color(0x40CCCCCC); ...@@ -29,41 +29,109 @@ const Color _kDarkThemeSplashColor = const Color(0x40CCCCCC);
class ThemeData { class ThemeData {
ThemeData({ ThemeData.raw({
this.brightness,
this.primarySwatch,
this.primaryColor,
this.primaryColorBrightness,
this.canvasColor,
this.cardColor,
this.dividerColor,
this.highlightColor,
this.splashColor,
this.unselectedColor,
this.disabledColor,
this.accentColor,
this.accentColorBrightness,
this.indicatorColor,
this.hintColor,
this.hintOpacity,
this.text,
this.primaryTextTheme,
this.primaryIconTheme
}) {
assert(brightness != null);
// primarySwatch can be null; TODO(ianh): see https://github.com/flutter/flutter/issues/1277
assert(primaryColor != null);
assert(primaryColorBrightness != null);
assert(canvasColor != null);
assert(cardColor != null);
assert(dividerColor != null);
assert(highlightColor != null);
assert(splashColor != null);
assert(unselectedColor != null);
assert(disabledColor != null);
assert(accentColor != null);
assert(accentColorBrightness != null);
assert(indicatorColor != null);
assert(hintColor != null);
assert(hintOpacity != null);
assert(text != null);
assert(primaryTextTheme != null);
assert(primaryIconTheme != null);
}
factory ThemeData({
ThemeBrightness brightness: ThemeBrightness.light, ThemeBrightness brightness: ThemeBrightness.light,
Map<int, Color> primarySwatch, Map<int, Color> primarySwatch,
Color primaryColor,
ThemeBrightness primaryColorBrightness,
Color canvasColor,
Color cardColor,
Color dividerColor,
Color highlightColor,
Color splashColor,
Color unselectedColor,
Color disabledColor,
Color accentColor, Color accentColor,
this.accentColorBrightness: ThemeBrightness.dark, ThemeBrightness accentColorBrightness: ThemeBrightness.dark,
TextTheme text Color indicatorColor,
}): this.brightness = brightness, Color hintColor,
this.primarySwatch = primarySwatch, double hintOpacity,
primaryColorBrightness = primarySwatch == null ? brightness : ThemeBrightness.dark, TextTheme text,
canvasColor = brightness == ThemeBrightness.dark ? Colors.grey[850] : Colors.grey[50], TextTheme primaryTextTheme,
cardColor = brightness == ThemeBrightness.dark ? Colors.grey[800] : Colors.white, IconThemeData primaryIconTheme
dividerColor = brightness == ThemeBrightness.dark ? const Color(0x1FFFFFFF) : const Color(0x1F000000), }) {
// Some users want the pre-multiplied color, others just want the opacity. // brightness default is in the arguments list
hintColor = brightness == ThemeBrightness.dark ? const Color(0x42FFFFFF) : const Color(0x4C000000), bool isDark = brightness == ThemeBrightness.dark;
hintOpacity = brightness == ThemeBrightness.dark ? 0.26 : 0.30, primaryColor ??= primarySwatch == null ? isDark ? Colors.grey[900] : Colors.grey[100] : primarySwatch[500];
highlightColor = brightness == ThemeBrightness.dark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor, primaryColorBrightness ??= primarySwatch == null ? brightness : ThemeBrightness.dark /* swatch[500] is always dark */;
splashColor = brightness == ThemeBrightness.dark ? _kDarkThemeSplashColor : _kLightThemeSplashColor, canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50];
text = brightness == ThemeBrightness.dark ? Typography.white : Typography.black { cardColor ??= isDark ? Colors.grey[800] : Colors.white;
assert(brightness != null); dividerColor ??= isDark ? const Color(0x1FFFFFFF) : const Color(0x1F000000);
highlightColor ??= isDark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor;
if (primarySwatch == null) { splashColor ??= isDark ? _kDarkThemeSplashColor : _kLightThemeSplashColor;
if (brightness == ThemeBrightness.dark) { unselectedColor ??= isDark ? Colors.white70 : Colors.black54;
_primaryColor = Colors.grey[900]; disabledColor ??= isDark ? Colors.white30 : Colors.black26;
} else { accentColor ??= primarySwatch == null ? Colors.blue[500] : primarySwatch[500];
_primaryColor = Colors.grey[100]; // accentColorBrightness default is in the arguments list
} indicatorColor ??= accentColor == primaryColor ? Colors.white : accentColor;
} else { hintColor ??= isDark ? const Color(0x42FFFFFF) : const Color(0x4C000000);
_primaryColor = primarySwatch[500]; hintOpacity ??= hintColor != null ? hintColor.alpha / 0xFF : isDark ? 0.26 : 0.30;
} text ??= isDark ? Typography.white : Typography.black;
primaryTextTheme ??= primaryColorBrightness == ThemeBrightness.dark ? Typography.white : Typography.black;
if (accentColor == null) { primaryIconTheme ??= primaryColorBrightness == ThemeBrightness.dark ? const IconThemeData(color: IconThemeColor.white) : const IconThemeData(color: IconThemeColor.black);
_accentColor = primarySwatch == null ? Colors.blue[500] : primarySwatch[500]; return new ThemeData.raw(
} else { brightness: brightness,
_accentColor = accentColor; primarySwatch: primarySwatch,
} primaryColor: primaryColor,
primaryColorBrightness: primaryColorBrightness,
canvasColor: canvasColor,
cardColor: cardColor,
dividerColor: dividerColor,
highlightColor: highlightColor,
splashColor: splashColor,
unselectedColor: unselectedColor,
disabledColor: disabledColor,
accentColor: accentColor,
accentColorBrightness: accentColorBrightness,
indicatorColor: indicatorColor,
hintColor: hintColor,
hintOpacity: hintOpacity,
text: text,
primaryTextTheme: primaryTextTheme,
primaryIconTheme: primaryIconTheme
);
} }
factory ThemeData.light() => new ThemeData(primarySwatch: Colors.blue, brightness: ThemeBrightness.light); factory ThemeData.light() => new ThemeData(primarySwatch: Colors.blue, brightness: ThemeBrightness.light);
...@@ -78,93 +146,129 @@ class ThemeData { ...@@ -78,93 +146,129 @@ class ThemeData {
/// all dark. When the ThemeBrightness is light, the canvas and card colors /// all dark. When the ThemeBrightness is light, the canvas and card colors
/// are bright, and the primary color's darkness varies as described by /// are bright, and the primary color's darkness varies as described by
/// primaryColorBrightness. The primaryColor does not contrast well with the /// primaryColorBrightness. The primaryColor does not contrast well with the
/// card and canvas colors when the brightness is dask; when the birghtness is /// card and canvas colors when the brightness is dark; when the brightness is
/// dark, use Colors.white or the accentColor for a contrasting color. /// dark, use Colors.white or the accentColor for a contrasting color.
final ThemeBrightness brightness; final ThemeBrightness brightness;
final Map<int, Color> primarySwatch; final Map<int, Color> primarySwatch;
final Color canvasColor;
final Color cardColor;
final Color dividerColor;
final Color hintColor;
final Color highlightColor;
final Color splashColor;
final double hintOpacity;
/// Text with a color that contrasts with the card and canvas colors.
final TextTheme text;
/// The background colour for major parts of the app (toolbars, tab bars, etc) /// The background colour for major parts of the app (toolbars, tab bars, etc)
Color get primaryColor => _primaryColor; final Color primaryColor;
Color _primaryColor;
/// The brightness of the primaryColor. Used to determine the colour of text and /// The brightness of the primaryColor. Used to determine the colour of text and
/// icons placed on top of the primary color (e.g. toolbar text). /// icons placed on top of the primary color (e.g. toolbar text).
final ThemeBrightness primaryColorBrightness; final ThemeBrightness primaryColorBrightness;
/// A text theme that contrasts with the primary color. final Color canvasColor;
TextTheme get primaryTextTheme { final Color cardColor;
if (primaryColorBrightness == ThemeBrightness.dark) final Color dividerColor;
return Typography.white; final Color highlightColor;
return Typography.black; final Color splashColor;
} final Color unselectedColor;
final Color disabledColor;
IconThemeData get primaryIconTheme {
if (primaryColorBrightness == ThemeBrightness.dark)
return const IconThemeData(color: IconThemeColor.white);
return const IconThemeData(color: IconThemeColor.black);
}
Color get unselectedColor {
if (brightness == ThemeBrightness.dark)
return Colors.white70;
return Colors.black54;
}
Color get disabledColor {
if (brightness == ThemeBrightness.dark)
return Colors.white30;
return Colors.black26;
}
/// The foreground color for widgets (knobs, text, etc) /// The foreground color for widgets (knobs, text, etc)
Color get accentColor => _accentColor; final Color accentColor;
Color _accentColor;
/// The brightness of the accentColor. Used to determine the colour of text /// The brightness of the accentColor. Used to determine the colour of text
/// and icons placed on top of the accent color (e.g. the icons on a floating /// and icons placed on top of the accent color (e.g. the icons on a floating
/// action button). /// action button).
final ThemeBrightness accentColorBrightness; final ThemeBrightness accentColorBrightness;
/// The color of the selected tab indicator in a tab strip.
final Color indicatorColor;
// Some users want the pre-multiplied color, others just want the opacity.
final Color hintColor;
final double hintOpacity;
/// Text with a color that contrasts with the card and canvas colors.
final TextTheme text;
/// A text theme that contrasts with the primary color.
final TextTheme primaryTextTheme;
final IconThemeData primaryIconTheme;
static ThemeData lerp(ThemeData begin, ThemeData end, double t) {
Map<int, Color> primarySwatch;
if (begin.primarySwatch != null && end.primarySwatch != null) {
primarySwatch = <int, Color>{};
for (int index in begin.primarySwatch.keys) {
if (!end.primarySwatch.containsKey(index))
continue;
primarySwatch[index] = Color.lerp(begin.primarySwatch[index], end.primarySwatch[index], t);
}
}
return new ThemeData.raw(
brightness: t < 0.5 ? begin.brightness : end.brightness,
primarySwatch: primarySwatch,
primaryColor: Color.lerp(begin.primaryColor, end.primaryColor, t),
primaryColorBrightness: t < 0.5 ? begin.primaryColorBrightness : end.primaryColorBrightness,
canvasColor: Color.lerp(begin.canvasColor, end.canvasColor, 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),
unselectedColor: Color.lerp(begin.unselectedColor, end.unselectedColor, t),
disabledColor: Color.lerp(begin.disabledColor, end.disabledColor, 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),
hintOpacity: lerpDouble(begin.hintOpacity, end.hintOpacity, t),
text: TextTheme.lerp(begin.text, end.text, t),
primaryTextTheme: TextTheme.lerp(begin.primaryTextTheme, end.primaryTextTheme, t),
primaryIconTheme: IconThemeData.lerp(begin.primaryIconTheme, end.primaryIconTheme, t)
);
}
bool operator==(Object other) { bool operator==(Object other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType)
return false; return false;
ThemeData otherData = other; ThemeData otherData = other;
return (otherData.brightness == brightness) && return (otherData.brightness == brightness) &&
(otherData.primarySwatch == primarySwatch) && (otherData.primarySwatch == primarySwatch) &&
(otherData.primaryColor == primaryColor) &&
(otherData.primaryColorBrightness == primaryColorBrightness) &&
(otherData.canvasColor == canvasColor) && (otherData.canvasColor == canvasColor) &&
(otherData.cardColor == cardColor) && (otherData.cardColor == cardColor) &&
(otherData.dividerColor == dividerColor) && (otherData.dividerColor == dividerColor) &&
(otherData.hintColor == hintColor) &&
(otherData.highlightColor == highlightColor) && (otherData.highlightColor == highlightColor) &&
(otherData.splashColor == splashColor) &&
(otherData.unselectedColor == unselectedColor) &&
(otherData.disabledColor == disabledColor) &&
(otherData.accentColor == accentColor) &&
(otherData.accentColorBrightness == accentColorBrightness) &&
(otherData.indicatorColor == indicatorColor) &&
(otherData.hintColor == hintColor) &&
(otherData.hintOpacity == hintOpacity) && (otherData.hintOpacity == hintOpacity) &&
(otherData.text == text) && (otherData.text == text) &&
(otherData.primaryColorBrightness == primaryColorBrightness) && (otherData.primaryTextTheme == primaryTextTheme) &&
(otherData.accentColorBrightness == accentColorBrightness); (otherData.primaryIconTheme == primaryIconTheme);
} }
int get hashCode { int get hashCode {
return hashValues( return hashValues(
brightness, brightness,
primarySwatch, hashList(primarySwatch.keys),
hashList(primarySwatch.values),
primaryColor,
primaryColorBrightness,
canvasColor, canvasColor,
cardColor, cardColor,
dividerColor, dividerColor,
hintColor,
highlightColor, highlightColor,
splashColor,
unselectedColor,
disabledColor,
accentColor,
accentColorBrightness,
indicatorColor,
hintColor,
hintOpacity, hintOpacity,
text, text,
primaryColorBrightness, primaryTextTheme,
accentColorBrightness primaryIconTheme
); );
} }
......
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
// See http://www.google.com/design/spec/style/typography.html // See http://www.google.com/design/spec/style/typography.html
import 'dart:ui' show Color;
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
import 'colors.dart'; import 'colors.dart';
...@@ -13,8 +11,11 @@ import 'colors.dart'; ...@@ -13,8 +11,11 @@ import 'colors.dart';
// TODO(eseidel): Font weights are supposed to be language relative! // TODO(eseidel): Font weights are supposed to be language relative!
// TODO(jackson): Baseline should be language relative! // TODO(jackson): Baseline should be language relative!
// These values are for English-like text. // These values are for English-like text.
// TODO(ianh): There's no font-family specified here.
class TextTheme { class TextTheme {
const TextTheme._(this.display4, this.display3, this.display2, this.display1, this.headline, this.title, this.subhead, this.body2, this.body1, this.caption, this.button);
const TextTheme._black() const TextTheme._black()
: display4 = const TextStyle(inherit: false, fontSize: 112.0, fontWeight: FontWeight.w100, color: Colors.black54, textBaseline: TextBaseline.alphabetic), : display4 = const TextStyle(inherit: false, fontSize: 112.0, fontWeight: FontWeight.w100, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
display3 = const TextStyle(inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic), display3 = const TextStyle(inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
...@@ -53,26 +54,26 @@ class TextTheme { ...@@ -53,26 +54,26 @@ class TextTheme {
final TextStyle caption; final TextStyle caption;
final TextStyle button; final TextStyle button;
static TextTheme lerp(TextTheme begin, TextTheme end, double t) {
return new TextTheme._(
TextStyle.lerp(begin.display4, end.display4, t),
TextStyle.lerp(begin.display3, end.display3, t),
TextStyle.lerp(begin.display2, end.display2, t),
TextStyle.lerp(begin.display1, end.display1, t),
TextStyle.lerp(begin.headline, end.headline, t),
TextStyle.lerp(begin.title, end.title, t),
TextStyle.lerp(begin.subhead, end.subhead, t),
TextStyle.lerp(begin.body2, end.body2, t),
TextStyle.lerp(begin.body1, end.body1, t),
TextStyle.lerp(begin.caption, end.caption, t),
TextStyle.lerp(begin.button, end.button, t)
);
}
} }
class Typography { class Typography {
Typography._(); Typography._();
static const TextTheme black = const TextTheme._black(); static const TextTheme black = const TextTheme._black();
static const TextTheme white = const TextTheme._white(); static const TextTheme white = const TextTheme._white();
// TODO(abarth): Maybe this should be hard-coded in Scaffold?
static const String typeface = 'font-family: sans-serif';
// TODO(ianh): Remove this when we remove fn2, now that it's hard-coded in App.
static const TextStyle error = const TextStyle(
color: const Color(0xD0FF0000),
fontFamily: 'monospace',
fontSize: 48.0,
fontWeight: FontWeight.w900,
textAlign: TextAlign.right,
decoration: TextDecoration.underline,
decorationColor: const Color(0xFFFF00),
decorationStyle: TextDecorationStyle.double
);
} }
...@@ -127,6 +127,31 @@ class TextStyle { ...@@ -127,6 +127,31 @@ class TextStyle {
); );
} }
/// 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);
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),
// TODO(ianh): Replace next line with "fontWeight: FontWeight.lerp(begin.fontWeight, end.fontWeight, t)," once engine is revved
fontWeight: FontWeight.values[ui.lerpDouble(begin?.fontWeight.index ?? FontWeight.normal.index, end?.fontWeight.index ?? FontWeight.normal.index, t.clamp(0.0, 1.0)).round()],
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),
textAlign: t < 0.5 ? begin.textAlign : end.textAlign,
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
);
}
ui.TextStyle get textStyle { ui.TextStyle get textStyle {
return new ui.TextStyle( return new ui.TextStyle(
color: color, color: color,
......
...@@ -317,7 +317,7 @@ class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> ...@@ -317,7 +317,7 @@ class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer>
} }
/// Animated version of [Positioned] which automatically transitions the child's /// Animated version of [Positioned] which automatically transitions the child's
/// position over a given duration whenever the given positon changes. /// position over a given duration whenever the given position changes.
/// ///
/// Only works if it's the child of a [Stack]. /// Only works if it's the child of a [Stack].
class AnimatedPositioned extends AnimatedWidgetBase { class AnimatedPositioned extends AnimatedWidgetBase {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment