mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Merge pull request #1282 from Hixie/animated-theme
Reimplement the theme transition animation by actually animating the Theme
This commit is contained in:
commit
663ccd8b1b
@ -8,6 +8,12 @@ class IconThemeData {
|
||||
const IconThemeData({ this.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) {
|
||||
if (other is! IconThemeData)
|
||||
return false;
|
||||
|
||||
@ -160,12 +160,18 @@ class _MaterialState extends State<Material> {
|
||||
curve: Curves.ease,
|
||||
duration: kThemeChangeDuration,
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: backgroundColor,
|
||||
borderRadius: kMaterialEdges[config.type],
|
||||
boxShadow: config.elevation == 0 ? null : elevationToShadow[config.elevation],
|
||||
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;
|
||||
|
||||
@ -141,8 +141,9 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
|
||||
data: new MediaQueryData(size: _size),
|
||||
child: new LocaleQuery(
|
||||
data: _localeData,
|
||||
child: new Theme(
|
||||
child: new AnimatedTheme(
|
||||
data: theme,
|
||||
duration: kThemeAnimationDuration,
|
||||
child: new DefaultTextStyle(
|
||||
style: _errorTextStyle,
|
||||
child: new DefaultAssetBundle(
|
||||
|
||||
@ -717,9 +717,17 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
|
||||
|
||||
ThemeData themeData = Theme.of(context);
|
||||
Color backgroundColor = Material.of(context).color;
|
||||
Color indicatorColor = themeData.accentColor;
|
||||
if (indicatorColor == backgroundColor)
|
||||
Color indicatorColor = themeData.indicatorColor;
|
||||
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;
|
||||
}
|
||||
|
||||
TextStyle textStyle = themeData.primaryTextTheme.body1;
|
||||
IconThemeData iconTheme = themeData.primaryIconTheme;
|
||||
|
||||
@ -2,12 +2,15 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'theme_data.dart';
|
||||
|
||||
export 'theme_data.dart' show ThemeData, ThemeBrightness;
|
||||
|
||||
const kThemeAnimationDuration = const Duration(milliseconds: 200);
|
||||
|
||||
class Theme extends InheritedWidget {
|
||||
Theme({
|
||||
Key key,
|
||||
@ -37,3 +40,55 @@ class Theme extends InheritedWidget {
|
||||
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 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui' show Color, hashValues;
|
||||
import 'dart:ui' show Color, hashValues, hashList, lerpDouble;
|
||||
|
||||
import 'colors.dart';
|
||||
import 'icon_theme_data.dart';
|
||||
@ -29,41 +29,109 @@ const Color _kDarkThemeSplashColor = const Color(0x40CCCCCC);
|
||||
|
||||
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,
|
||||
Map<int, Color> primarySwatch,
|
||||
Color primaryColor,
|
||||
ThemeBrightness primaryColorBrightness,
|
||||
Color canvasColor,
|
||||
Color cardColor,
|
||||
Color dividerColor,
|
||||
Color highlightColor,
|
||||
Color splashColor,
|
||||
Color unselectedColor,
|
||||
Color disabledColor,
|
||||
Color accentColor,
|
||||
this.accentColorBrightness: ThemeBrightness.dark,
|
||||
TextTheme text
|
||||
}): this.brightness = brightness,
|
||||
this.primarySwatch = primarySwatch,
|
||||
primaryColorBrightness = primarySwatch == null ? brightness : ThemeBrightness.dark,
|
||||
canvasColor = brightness == ThemeBrightness.dark ? Colors.grey[850] : Colors.grey[50],
|
||||
cardColor = brightness == ThemeBrightness.dark ? Colors.grey[800] : Colors.white,
|
||||
dividerColor = brightness == ThemeBrightness.dark ? const Color(0x1FFFFFFF) : const Color(0x1F000000),
|
||||
// Some users want the pre-multiplied color, others just want the opacity.
|
||||
hintColor = brightness == ThemeBrightness.dark ? const Color(0x42FFFFFF) : const Color(0x4C000000),
|
||||
hintOpacity = brightness == ThemeBrightness.dark ? 0.26 : 0.30,
|
||||
highlightColor = brightness == ThemeBrightness.dark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor,
|
||||
splashColor = brightness == ThemeBrightness.dark ? _kDarkThemeSplashColor : _kLightThemeSplashColor,
|
||||
text = brightness == ThemeBrightness.dark ? Typography.white : Typography.black {
|
||||
assert(brightness != null);
|
||||
|
||||
if (primarySwatch == null) {
|
||||
if (brightness == ThemeBrightness.dark) {
|
||||
_primaryColor = Colors.grey[900];
|
||||
} else {
|
||||
_primaryColor = Colors.grey[100];
|
||||
}
|
||||
} else {
|
||||
_primaryColor = primarySwatch[500];
|
||||
}
|
||||
|
||||
if (accentColor == null) {
|
||||
_accentColor = primarySwatch == null ? Colors.blue[500] : primarySwatch[500];
|
||||
} else {
|
||||
_accentColor = accentColor;
|
||||
}
|
||||
ThemeBrightness accentColorBrightness: ThemeBrightness.dark,
|
||||
Color indicatorColor,
|
||||
Color hintColor,
|
||||
double hintOpacity,
|
||||
TextTheme text,
|
||||
TextTheme primaryTextTheme,
|
||||
IconThemeData primaryIconTheme
|
||||
}) {
|
||||
// brightness default is in the arguments list
|
||||
bool isDark = brightness == ThemeBrightness.dark;
|
||||
primaryColor ??= primarySwatch == null ? isDark ? Colors.grey[900] : Colors.grey[100] : primarySwatch[500];
|
||||
primaryColorBrightness ??= primarySwatch == null ? brightness : ThemeBrightness.dark /* swatch[500] is always dark */;
|
||||
canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50];
|
||||
cardColor ??= isDark ? Colors.grey[800] : Colors.white;
|
||||
dividerColor ??= isDark ? const Color(0x1FFFFFFF) : const Color(0x1F000000);
|
||||
highlightColor ??= isDark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor;
|
||||
splashColor ??= isDark ? _kDarkThemeSplashColor : _kLightThemeSplashColor;
|
||||
unselectedColor ??= isDark ? Colors.white70 : Colors.black54;
|
||||
disabledColor ??= isDark ? Colors.white30 : Colors.black26;
|
||||
accentColor ??= primarySwatch == null ? Colors.blue[500] : primarySwatch[500];
|
||||
// accentColorBrightness default is in the arguments list
|
||||
indicatorColor ??= accentColor == primaryColor ? Colors.white : accentColor;
|
||||
hintColor ??= isDark ? const Color(0x42FFFFFF) : const Color(0x4C000000);
|
||||
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;
|
||||
primaryIconTheme ??= primaryColorBrightness == ThemeBrightness.dark ? const IconThemeData(color: IconThemeColor.white) : const IconThemeData(color: IconThemeColor.black);
|
||||
return new ThemeData.raw(
|
||||
brightness: brightness,
|
||||
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);
|
||||
@ -78,93 +146,129 @@ class ThemeData {
|
||||
/// all dark. When the ThemeBrightness is light, the canvas and card colors
|
||||
/// are bright, and the primary color's darkness varies as described by
|
||||
/// 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.
|
||||
final ThemeBrightness brightness;
|
||||
|
||||
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)
|
||||
Color get primaryColor => _primaryColor;
|
||||
Color _primaryColor;
|
||||
final Color primaryColor;
|
||||
|
||||
/// 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).
|
||||
final ThemeBrightness primaryColorBrightness;
|
||||
|
||||
/// A text theme that contrasts with the primary color.
|
||||
TextTheme get primaryTextTheme {
|
||||
if (primaryColorBrightness == ThemeBrightness.dark)
|
||||
return Typography.white;
|
||||
return Typography.black;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
final Color canvasColor;
|
||||
final Color cardColor;
|
||||
final Color dividerColor;
|
||||
final Color highlightColor;
|
||||
final Color splashColor;
|
||||
final Color unselectedColor;
|
||||
final Color disabledColor;
|
||||
|
||||
/// The foreground color for widgets (knobs, text, etc)
|
||||
Color get accentColor => _accentColor;
|
||||
Color _accentColor;
|
||||
final Color accentColor;
|
||||
|
||||
/// 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
|
||||
/// action button).
|
||||
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) {
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
ThemeData otherData = other;
|
||||
return (otherData.brightness == brightness) &&
|
||||
(otherData.primarySwatch == primarySwatch) &&
|
||||
(otherData.primaryColor == primaryColor) &&
|
||||
(otherData.primaryColorBrightness == primaryColorBrightness) &&
|
||||
(otherData.canvasColor == canvasColor) &&
|
||||
(otherData.cardColor == cardColor) &&
|
||||
(otherData.dividerColor == dividerColor) &&
|
||||
(otherData.hintColor == hintColor) &&
|
||||
(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.text == text) &&
|
||||
(otherData.primaryColorBrightness == primaryColorBrightness) &&
|
||||
(otherData.accentColorBrightness == accentColorBrightness);
|
||||
(otherData.primaryTextTheme == primaryTextTheme) &&
|
||||
(otherData.primaryIconTheme == primaryIconTheme);
|
||||
}
|
||||
int get hashCode {
|
||||
return hashValues(
|
||||
brightness,
|
||||
primarySwatch,
|
||||
hashList(primarySwatch.keys),
|
||||
hashList(primarySwatch.values),
|
||||
primaryColor,
|
||||
primaryColorBrightness,
|
||||
canvasColor,
|
||||
cardColor,
|
||||
dividerColor,
|
||||
hintColor,
|
||||
highlightColor,
|
||||
splashColor,
|
||||
unselectedColor,
|
||||
disabledColor,
|
||||
accentColor,
|
||||
accentColorBrightness,
|
||||
indicatorColor,
|
||||
hintColor,
|
||||
hintOpacity,
|
||||
text,
|
||||
primaryColorBrightness,
|
||||
accentColorBrightness
|
||||
primaryTextTheme,
|
||||
primaryIconTheme
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
|
||||
// See http://www.google.com/design/spec/style/typography.html
|
||||
|
||||
import 'dart:ui' show Color;
|
||||
|
||||
import 'package:flutter/painting.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
@ -13,8 +11,11 @@ import 'colors.dart';
|
||||
// TODO(eseidel): Font weights are supposed to be language relative!
|
||||
// TODO(jackson): Baseline should be language relative!
|
||||
// These values are for English-like text.
|
||||
// TODO(ianh): There's no font-family specified here.
|
||||
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()
|
||||
: 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),
|
||||
@ -53,26 +54,26 @@ class TextTheme {
|
||||
final TextStyle caption;
|
||||
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 {
|
||||
Typography._();
|
||||
|
||||
static const TextTheme black = const TextTheme._black();
|
||||
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 {
|
||||
);
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
return new ui.TextStyle(
|
||||
color: color,
|
||||
|
||||
@ -317,7 +317,7 @@ class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer>
|
||||
}
|
||||
|
||||
/// 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].
|
||||
class AnimatedPositioned extends AnimatedWidgetBase {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user