ThemeData: optimize by removing polymorphism and caching; fix merging (#12249)
* optimize ThemeData: make it monomorphic, memoize result * address comments * RLU cache; fix text theme merging * use FIFO cache for ThemeData; use HashMap to store inherited widgets * address comments
This commit is contained in:
parent
0189696e4b
commit
cc3f5767f4
@ -48,6 +48,7 @@ const Color _kDarkThemeSplashColor = const Color(0x40CCCCCC);
|
|||||||
/// Use this class to configure a [Theme] widget.
|
/// Use this class to configure a [Theme] widget.
|
||||||
///
|
///
|
||||||
/// To obtain the current theme, use [Theme.of].
|
/// To obtain the current theme, use [Theme.of].
|
||||||
|
@immutable
|
||||||
class ThemeData {
|
class ThemeData {
|
||||||
/// Create a ThemeData given a set of preferred values.
|
/// Create a ThemeData given a set of preferred values.
|
||||||
///
|
///
|
||||||
@ -418,50 +419,80 @@ class ThemeData {
|
|||||||
IconThemeData accentIconTheme,
|
IconThemeData accentIconTheme,
|
||||||
TargetPlatform platform,
|
TargetPlatform platform,
|
||||||
}) {
|
}) {
|
||||||
return _copyThemeDataWith(
|
return new ThemeData.raw(
|
||||||
this,
|
brightness: brightness ?? this.brightness,
|
||||||
brightness: brightness,
|
primaryColor: primaryColor ?? this.primaryColor,
|
||||||
primaryColor: primaryColor,
|
primaryColorBrightness: primaryColorBrightness ?? this.primaryColorBrightness,
|
||||||
primaryColorBrightness: primaryColorBrightness,
|
accentColor: accentColor ?? this.accentColor,
|
||||||
accentColor: accentColor,
|
accentColorBrightness: accentColorBrightness ?? this.accentColorBrightness,
|
||||||
accentColorBrightness: accentColorBrightness,
|
canvasColor: canvasColor ?? this.canvasColor,
|
||||||
canvasColor: canvasColor,
|
scaffoldBackgroundColor: scaffoldBackgroundColor ?? this.scaffoldBackgroundColor,
|
||||||
scaffoldBackgroundColor: scaffoldBackgroundColor,
|
cardColor: cardColor ?? this.cardColor,
|
||||||
cardColor: cardColor,
|
dividerColor: dividerColor ?? this.dividerColor,
|
||||||
dividerColor: dividerColor,
|
highlightColor: highlightColor ?? this.highlightColor,
|
||||||
highlightColor: highlightColor,
|
splashColor: splashColor ?? this.splashColor,
|
||||||
splashColor: splashColor,
|
selectedRowColor: selectedRowColor ?? this.selectedRowColor,
|
||||||
selectedRowColor: selectedRowColor,
|
unselectedWidgetColor: unselectedWidgetColor ?? this.unselectedWidgetColor,
|
||||||
unselectedWidgetColor: unselectedWidgetColor,
|
disabledColor: disabledColor ?? this.disabledColor,
|
||||||
disabledColor: disabledColor,
|
buttonColor: buttonColor ?? this.buttonColor,
|
||||||
buttonColor: buttonColor,
|
secondaryHeaderColor: secondaryHeaderColor ?? this.secondaryHeaderColor,
|
||||||
secondaryHeaderColor: secondaryHeaderColor,
|
textSelectionColor: textSelectionColor ?? this.textSelectionColor,
|
||||||
textSelectionColor: textSelectionColor,
|
textSelectionHandleColor: textSelectionHandleColor ?? this.textSelectionHandleColor,
|
||||||
textSelectionHandleColor: textSelectionHandleColor,
|
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||||
backgroundColor: backgroundColor,
|
dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor,
|
||||||
dialogBackgroundColor: dialogBackgroundColor,
|
indicatorColor: indicatorColor ?? this.indicatorColor,
|
||||||
indicatorColor: indicatorColor,
|
hintColor: hintColor ?? this.hintColor,
|
||||||
hintColor: hintColor,
|
errorColor: errorColor ?? this.errorColor,
|
||||||
errorColor: errorColor,
|
textTheme: textTheme ?? this.textTheme,
|
||||||
textTheme: textTheme,
|
primaryTextTheme: primaryTextTheme ?? this.primaryTextTheme,
|
||||||
primaryTextTheme: primaryTextTheme,
|
accentTextTheme: accentTextTheme ?? this.accentTextTheme,
|
||||||
accentTextTheme: accentTextTheme,
|
iconTheme: iconTheme ?? this.iconTheme,
|
||||||
iconTheme: iconTheme,
|
primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme,
|
||||||
primaryIconTheme: primaryIconTheme,
|
accentIconTheme: accentIconTheme ?? this.accentIconTheme,
|
||||||
accentIconTheme: accentIconTheme,
|
platform: platform ?? this.platform,
|
||||||
platform: platform,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The number 5 was chosen without any real science or research behind it. It
|
||||||
|
// just seemed like a number that's not too big (we should be able to fit 5
|
||||||
|
// copies of ThemeData in memory comfortably) and not too small (most apps
|
||||||
|
// shouldn't have more than 5 theme/localization pairs).
|
||||||
|
static const int _localizedThemeDataCacheSize = 5;
|
||||||
|
|
||||||
|
/// Caches localized themes to speed up the [localize] method.
|
||||||
|
static final _FifoCache<_IdentityThemeDataCacheKey, ThemeData> _localizedThemeDataCache = new _FifoCache<_IdentityThemeDataCacheKey, ThemeData>(_localizedThemeDataCacheSize);
|
||||||
|
|
||||||
/// Returns a new theme built by merging [baseTheme] into the text geometry
|
/// Returns a new theme built by merging [baseTheme] into the text geometry
|
||||||
/// provided by the [localTextGeometry].
|
/// provided by the [localTextGeometry].
|
||||||
///
|
///
|
||||||
/// The [TextStyle.inherit] field in the text styles provided by
|
/// The [TextStyle.inherit] field in the text styles provided by
|
||||||
/// [localTextGeometry] must be set to true.
|
/// [localTextGeometry] must be set to true.
|
||||||
static ThemeData localize(ThemeData baseTheme, TextTheme localTextGeometry) {
|
static ThemeData localize(ThemeData baseTheme, TextTheme localTextGeometry) {
|
||||||
|
// WARNING: this method memoizes the result in a cache based on the
|
||||||
|
// previously seen baseTheme and localTextGeometry. Memoization is safe
|
||||||
|
// because all inputs and outputs of this function are deeply immutable, and
|
||||||
|
// the computations are referentially transparent. It only short-circuits
|
||||||
|
// the computation if the new inputs are identical() to the previous ones.
|
||||||
|
// It does not use the == operator, which performs a costly deep comparison.
|
||||||
|
//
|
||||||
|
// When changing this method, make sure the memoization logic is correct.
|
||||||
|
// Remember:
|
||||||
|
//
|
||||||
|
// There are only two hard things in Computer Science: cache invalidation
|
||||||
|
// and naming things. -- Phil Karlton
|
||||||
assert(baseTheme != null);
|
assert(baseTheme != null);
|
||||||
assert(localTextGeometry != null);
|
assert(localTextGeometry != null);
|
||||||
return new _LocalizedThemeData(baseTheme, localTextGeometry);
|
|
||||||
|
return _localizedThemeDataCache.putIfAbsent(
|
||||||
|
new _IdentityThemeDataCacheKey(baseTheme, localTextGeometry),
|
||||||
|
() {
|
||||||
|
return baseTheme.copyWith(
|
||||||
|
primaryTextTheme: localTextGeometry.merge(baseTheme.primaryTextTheme),
|
||||||
|
accentTextTheme: localTextGeometry.merge(baseTheme.accentTextTheme),
|
||||||
|
textTheme: localTextGeometry.merge(baseTheme.textTheme),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// See <https://www.w3.org/TR/WCAG20/#relativeluminancedef>
|
// See <https://www.w3.org/TR/WCAG20/#relativeluminancedef>
|
||||||
@ -614,245 +645,58 @@ class ThemeData {
|
|||||||
String toString() => '$runtimeType(${ platform != defaultTargetPlatform ? "$platform " : ''}$brightness $primaryColor etc...)';
|
String toString() => '$runtimeType(${ platform != defaultTargetPlatform ? "$platform " : ''}$brightness $primaryColor etc...)';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A lazily evaluated theme that provides the properties of the given
|
class _IdentityThemeDataCacheKey {
|
||||||
/// [delegate] theme localized using the properties of the given
|
_IdentityThemeDataCacheKey(this.baseTheme, this.localTextGeometry);
|
||||||
/// [localTextGeometry].
|
|
||||||
///
|
|
||||||
/// The localization is done by merging of the [TextTheme] fields of the
|
|
||||||
/// [delegate] into the [localTextGeometry] and caching the results.
|
|
||||||
class _LocalizedThemeData implements ThemeData {
|
|
||||||
_LocalizedThemeData(this.delegate, this.localTextGeometry);
|
|
||||||
|
|
||||||
final ThemeData delegate;
|
final ThemeData baseTheme;
|
||||||
final TextTheme localTextGeometry;
|
final TextTheme localTextGeometry;
|
||||||
|
|
||||||
|
// Using XOR to make the hash function as fast as possible (e.g. Jenkins is
|
||||||
|
// noticeably slower).
|
||||||
@override
|
@override
|
||||||
Color get accentColor => delegate.accentColor;
|
int get hashCode => identityHashCode(baseTheme) ^ identityHashCode(localTextGeometry);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Brightness get accentColorBrightness => delegate.accentColorBrightness;
|
bool operator ==(Object other) {
|
||||||
|
// We are explicitly ignoring the possibility that the types might not
|
||||||
@override
|
// match in the interests of speed.
|
||||||
IconThemeData get accentIconTheme => delegate.accentIconTheme;
|
final _IdentityThemeDataCacheKey otherKey = other;
|
||||||
|
return identical(baseTheme, otherKey.baseTheme) && identical(localTextGeometry, otherKey.localTextGeometry);
|
||||||
@override
|
|
||||||
Color get backgroundColor => delegate.backgroundColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Brightness get brightness => delegate.brightness;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get buttonColor => delegate.buttonColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get canvasColor => delegate.canvasColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get cardColor => delegate.cardColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get dialogBackgroundColor => delegate.dialogBackgroundColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get disabledColor => delegate.disabledColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get dividerColor => delegate.dividerColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get errorColor => delegate.errorColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get highlightColor => delegate.highlightColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get hintColor => delegate.hintColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
IconThemeData get iconTheme => delegate.iconTheme;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get indicatorColor => delegate.indicatorColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
TargetPlatform get platform => delegate.platform;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get primaryColor => delegate.primaryColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Brightness get primaryColorBrightness => delegate.primaryColorBrightness;
|
|
||||||
|
|
||||||
@override
|
|
||||||
IconThemeData get primaryIconTheme => delegate.primaryIconTheme;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get scaffoldBackgroundColor => delegate.scaffoldBackgroundColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get secondaryHeaderColor => delegate.secondaryHeaderColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get selectedRowColor => delegate.selectedRowColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get splashColor => delegate.splashColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get textSelectionColor => delegate.textSelectionColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get textSelectionHandleColor => delegate.textSelectionHandleColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Color get unselectedWidgetColor => delegate.unselectedWidgetColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
TextTheme get primaryTextTheme => _primaryTextTheme ??= delegate.primaryTextTheme.merge(localTextGeometry);
|
|
||||||
TextTheme _primaryTextTheme;
|
|
||||||
|
|
||||||
@override
|
|
||||||
TextTheme get accentTextTheme => _accentTextTheme ??= delegate.accentTextTheme.merge(localTextGeometry);
|
|
||||||
TextTheme _accentTextTheme;
|
|
||||||
|
|
||||||
@override
|
|
||||||
TextTheme get textTheme => _textTheme ??= delegate.textTheme.merge(localTextGeometry);
|
|
||||||
TextTheme _textTheme;
|
|
||||||
|
|
||||||
/// This should be identical to [ThemeData.copyWith].
|
|
||||||
@override
|
|
||||||
ThemeData copyWith({
|
|
||||||
Brightness brightness,
|
|
||||||
Color primaryColor,
|
|
||||||
Brightness primaryColorBrightness,
|
|
||||||
Color accentColor,
|
|
||||||
Brightness accentColorBrightness,
|
|
||||||
Color canvasColor,
|
|
||||||
Color scaffoldBackgroundColor,
|
|
||||||
Color cardColor,
|
|
||||||
Color dividerColor,
|
|
||||||
Color highlightColor,
|
|
||||||
Color splashColor,
|
|
||||||
Color selectedRowColor,
|
|
||||||
Color unselectedWidgetColor,
|
|
||||||
Color disabledColor,
|
|
||||||
Color buttonColor,
|
|
||||||
Color secondaryHeaderColor,
|
|
||||||
Color textSelectionColor,
|
|
||||||
Color textSelectionHandleColor,
|
|
||||||
Color backgroundColor,
|
|
||||||
Color dialogBackgroundColor,
|
|
||||||
Color indicatorColor,
|
|
||||||
Color hintColor,
|
|
||||||
Color errorColor,
|
|
||||||
TextTheme textTheme,
|
|
||||||
TextTheme primaryTextTheme,
|
|
||||||
TextTheme accentTextTheme,
|
|
||||||
IconThemeData iconTheme,
|
|
||||||
IconThemeData primaryIconTheme,
|
|
||||||
IconThemeData accentIconTheme,
|
|
||||||
TargetPlatform platform,
|
|
||||||
}) {
|
|
||||||
return _copyThemeDataWith(
|
|
||||||
this,
|
|
||||||
brightness: brightness,
|
|
||||||
primaryColor: primaryColor,
|
|
||||||
primaryColorBrightness: primaryColorBrightness,
|
|
||||||
accentColor: accentColor,
|
|
||||||
accentColorBrightness: accentColorBrightness,
|
|
||||||
canvasColor: canvasColor,
|
|
||||||
scaffoldBackgroundColor: scaffoldBackgroundColor,
|
|
||||||
cardColor: cardColor,
|
|
||||||
dividerColor: dividerColor,
|
|
||||||
highlightColor: highlightColor,
|
|
||||||
splashColor: splashColor,
|
|
||||||
selectedRowColor: selectedRowColor,
|
|
||||||
unselectedWidgetColor: unselectedWidgetColor,
|
|
||||||
disabledColor: disabledColor,
|
|
||||||
buttonColor: buttonColor,
|
|
||||||
secondaryHeaderColor: secondaryHeaderColor,
|
|
||||||
textSelectionColor: textSelectionColor,
|
|
||||||
textSelectionHandleColor: textSelectionHandleColor,
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
dialogBackgroundColor: dialogBackgroundColor,
|
|
||||||
indicatorColor: indicatorColor,
|
|
||||||
hintColor: hintColor,
|
|
||||||
errorColor: errorColor,
|
|
||||||
textTheme: textTheme,
|
|
||||||
primaryTextTheme: primaryTextTheme,
|
|
||||||
accentTextTheme: accentTextTheme,
|
|
||||||
iconTheme: iconTheme,
|
|
||||||
primaryIconTheme: primaryIconTheme,
|
|
||||||
accentIconTheme: accentIconTheme,
|
|
||||||
platform: platform,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of [ThemeData.copyWith], shared with [_LocalizedThemeData.copyWith].
|
/// Cache of objects of limited size that uses the first in first out eviction
|
||||||
ThemeData _copyThemeDataWith(
|
/// strategy (a.k.a least recently inserted).
|
||||||
ThemeData base, {
|
///
|
||||||
@required Brightness brightness,
|
/// The key that was inserted before all other keys is evicted first, i.e. the
|
||||||
@required Color primaryColor,
|
/// one inserted least recently.
|
||||||
@required Brightness primaryColorBrightness,
|
class _FifoCache<K, V> {
|
||||||
@required Color accentColor,
|
_FifoCache(this._maximumSize)
|
||||||
@required Brightness accentColorBrightness,
|
: assert(_maximumSize != null && _maximumSize > 0);
|
||||||
@required Color canvasColor,
|
|
||||||
@required Color scaffoldBackgroundColor,
|
/// In Dart the map literal uses a linked hash-map implementation, whose keys
|
||||||
@required Color cardColor,
|
/// are stored such that [Map.keys] returns them in the order they were
|
||||||
@required Color dividerColor,
|
/// inserted.
|
||||||
@required Color highlightColor,
|
final Map<K, V> _cache = <K, V>{};
|
||||||
@required Color splashColor,
|
|
||||||
@required Color selectedRowColor,
|
/// Maximum number of entries to store in the cache.
|
||||||
@required Color unselectedWidgetColor,
|
///
|
||||||
@required Color disabledColor,
|
/// Once this many entries have been cached, the entry inserted least recently
|
||||||
@required Color buttonColor,
|
/// is evicted when adding a new entry.
|
||||||
@required Color secondaryHeaderColor,
|
final int _maximumSize;
|
||||||
@required Color textSelectionColor,
|
|
||||||
@required Color textSelectionHandleColor,
|
/// Returns the previously cached value for the given key, if available;
|
||||||
@required Color backgroundColor,
|
/// if not, calls the given callback to obtain it first.
|
||||||
@required Color dialogBackgroundColor,
|
///
|
||||||
@required Color indicatorColor,
|
/// The arguments must not be null.
|
||||||
@required Color hintColor,
|
V putIfAbsent(K key, V loader()) {
|
||||||
@required Color errorColor,
|
assert(key != null);
|
||||||
@required TextTheme textTheme,
|
assert(loader != null);
|
||||||
@required TextTheme primaryTextTheme,
|
final V result = _cache[key];
|
||||||
@required TextTheme accentTextTheme,
|
if (result != null)
|
||||||
@required IconThemeData iconTheme,
|
return result;
|
||||||
@required IconThemeData primaryIconTheme,
|
if (_cache.length == _maximumSize)
|
||||||
@required IconThemeData accentIconTheme,
|
_cache.remove(_cache.keys.first);
|
||||||
@required TargetPlatform platform,
|
return _cache[key] = loader();
|
||||||
}) {
|
}
|
||||||
return new ThemeData.raw(
|
|
||||||
brightness: brightness ?? base.brightness,
|
|
||||||
primaryColor: primaryColor ?? base.primaryColor,
|
|
||||||
primaryColorBrightness: primaryColorBrightness ?? base.primaryColorBrightness,
|
|
||||||
accentColor: accentColor ?? base.accentColor,
|
|
||||||
accentColorBrightness: accentColorBrightness ?? base.accentColorBrightness,
|
|
||||||
canvasColor: canvasColor ?? base.canvasColor,
|
|
||||||
scaffoldBackgroundColor: scaffoldBackgroundColor ?? base.scaffoldBackgroundColor,
|
|
||||||
cardColor: cardColor ?? base.cardColor,
|
|
||||||
dividerColor: dividerColor ?? base.dividerColor,
|
|
||||||
highlightColor: highlightColor ?? base.highlightColor,
|
|
||||||
splashColor: splashColor ?? base.splashColor,
|
|
||||||
selectedRowColor: selectedRowColor ?? base.selectedRowColor,
|
|
||||||
unselectedWidgetColor: unselectedWidgetColor ?? base.unselectedWidgetColor,
|
|
||||||
disabledColor: disabledColor ?? base.disabledColor,
|
|
||||||
buttonColor: buttonColor ?? base.buttonColor,
|
|
||||||
secondaryHeaderColor: secondaryHeaderColor ?? base.secondaryHeaderColor,
|
|
||||||
textSelectionColor: textSelectionColor ?? base.textSelectionColor,
|
|
||||||
textSelectionHandleColor: textSelectionHandleColor ?? base.textSelectionHandleColor,
|
|
||||||
backgroundColor: backgroundColor ?? base.backgroundColor,
|
|
||||||
dialogBackgroundColor: dialogBackgroundColor ?? base.dialogBackgroundColor,
|
|
||||||
indicatorColor: indicatorColor ?? base.indicatorColor,
|
|
||||||
hintColor: hintColor ?? base.hintColor,
|
|
||||||
errorColor: errorColor ?? base.errorColor,
|
|
||||||
textTheme: textTheme ?? base.textTheme,
|
|
||||||
primaryTextTheme: primaryTextTheme ?? base.primaryTextTheme,
|
|
||||||
accentTextTheme: accentTextTheme ?? base.accentTextTheme,
|
|
||||||
iconTheme: iconTheme ?? base.iconTheme,
|
|
||||||
primaryIconTheme: primaryIconTheme ?? base.primaryIconTheme,
|
|
||||||
accentIconTheme: accentIconTheme ?? base.accentIconTheme,
|
|
||||||
platform: platform ?? base.platform,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -353,59 +353,59 @@ class Typography {
|
|||||||
// TODO(yjbanov): implement font fallback (see "Font stack" at https://material.io/guidelines/style/typography.html)
|
// TODO(yjbanov): implement font fallback (see "Font stack" at https://material.io/guidelines/style/typography.html)
|
||||||
class _MaterialTextColorThemes {
|
class _MaterialTextColorThemes {
|
||||||
static const TextTheme blackMountainView = const TextTheme(
|
static const TextTheme blackMountainView = const TextTheme(
|
||||||
display4: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
|
display4: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
|
||||||
display3: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
|
display3: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
|
||||||
display2: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
|
display2: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
|
||||||
display1: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
|
display1: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
|
||||||
headline: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
|
headline: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
|
||||||
title : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
|
title : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
|
||||||
subhead : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
|
subhead : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
|
||||||
body2 : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
|
body2 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
|
||||||
body1 : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
|
body1 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
|
||||||
caption : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
|
caption : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
|
||||||
button : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
|
button : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
|
||||||
);
|
);
|
||||||
|
|
||||||
static const TextTheme whiteMountainView = const TextTheme(
|
static const TextTheme whiteMountainView = const TextTheme(
|
||||||
display4: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
|
display4: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
|
||||||
display3: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
|
display3: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
|
||||||
display2: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
|
display2: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
|
||||||
display1: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
|
display1: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
|
||||||
headline: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
|
headline: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
|
||||||
title : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
|
title : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
|
||||||
subhead : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
|
subhead : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
|
||||||
body2 : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
|
body2 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
|
||||||
body1 : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
|
body1 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
|
||||||
caption : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
|
caption : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
|
||||||
button : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
|
button : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
|
||||||
);
|
);
|
||||||
|
|
||||||
static const TextTheme blackCupertino = const TextTheme(
|
static const TextTheme blackCupertino = const TextTheme(
|
||||||
display4: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black54),
|
display4: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54),
|
||||||
display3: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black54),
|
display3: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54),
|
||||||
display2: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black54),
|
display2: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54),
|
||||||
display1: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black54),
|
display1: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54),
|
||||||
headline: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black87),
|
headline: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black87),
|
||||||
title : const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black87),
|
title : const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black87),
|
||||||
subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black87),
|
subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87),
|
||||||
body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black87),
|
body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87),
|
||||||
body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black87),
|
body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87),
|
||||||
caption : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black54),
|
caption : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black54),
|
||||||
button : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black87),
|
button : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87),
|
||||||
);
|
);
|
||||||
|
|
||||||
static const TextTheme whiteCupertino = const TextTheme(
|
static const TextTheme whiteCupertino = const TextTheme(
|
||||||
display4: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white70),
|
display4: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70),
|
||||||
display3: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white70),
|
display3: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70),
|
||||||
display2: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white70),
|
display2: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70),
|
||||||
display1: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white70),
|
display1: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70),
|
||||||
headline: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white),
|
headline: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white),
|
||||||
title : const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white),
|
title : const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white),
|
||||||
subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white),
|
subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white),
|
||||||
body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white),
|
body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white),
|
||||||
body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white),
|
body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white),
|
||||||
caption : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white70),
|
caption : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white70),
|
||||||
button : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white),
|
button : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3911,9 +3911,9 @@ class InheritedElement extends ProxyElement {
|
|||||||
assert(_active);
|
assert(_active);
|
||||||
final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
|
final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
|
||||||
if (incomingWidgets != null)
|
if (incomingWidgets != null)
|
||||||
_inheritedWidgets = new Map<Type, InheritedElement>.from(incomingWidgets);
|
_inheritedWidgets = new HashMap<Type, InheritedElement>.from(incomingWidgets);
|
||||||
else
|
else
|
||||||
_inheritedWidgets = <Type, InheritedElement>{};
|
_inheritedWidgets = new HashMap<Type, InheritedElement>();
|
||||||
_inheritedWidgets[widget.runtimeType] = this;
|
_inheritedWidgets[widget.runtimeType] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,12 +53,33 @@ void main() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
final dynamic localizedTheme = Theme.of(capturedContext);
|
expect(Theme.of(capturedContext), equals(ThemeData.localize(new ThemeData.fallback(), MaterialTextGeometry.englishLike)));
|
||||||
expect('${localizedTheme.runtimeType}', '_LocalizedThemeData');
|
|
||||||
expect(localizedTheme.delegate, equals(new ThemeData.fallback()));
|
|
||||||
expect(Theme.of(capturedContext, shadowThemeOnly: true), isNull);
|
expect(Theme.of(capturedContext, shadowThemeOnly: true), isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('ThemeData.localize memoizes the result', (WidgetTester tester) async {
|
||||||
|
final ThemeData light = new ThemeData.light();
|
||||||
|
final ThemeData dark = new ThemeData.dark();
|
||||||
|
|
||||||
|
// Same input, same output.
|
||||||
|
expect(
|
||||||
|
ThemeData.localize(light, MaterialTextGeometry.englishLike),
|
||||||
|
same(ThemeData.localize(light, MaterialTextGeometry.englishLike)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Different text geometry, different output.
|
||||||
|
expect(
|
||||||
|
ThemeData.localize(light, MaterialTextGeometry.englishLike),
|
||||||
|
isNot(same(ThemeData.localize(light, MaterialTextGeometry.tall))),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Different base theme, different output.
|
||||||
|
expect(
|
||||||
|
ThemeData.localize(light, MaterialTextGeometry.englishLike),
|
||||||
|
isNot(same(ThemeData.localize(dark, MaterialTextGeometry.englishLike))),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('PopupMenu inherits shadowed app theme', (WidgetTester tester) async {
|
testWidgets('PopupMenu inherits shadowed app theme', (WidgetTester tester) async {
|
||||||
// Regression test for https://github.com/flutter/flutter/issues/5572
|
// Regression test for https://github.com/flutter/flutter/issues/5572
|
||||||
final Key popupMenuButtonKey = new UniqueKey();
|
final Key popupMenuButtonKey = new UniqueKey();
|
||||||
@ -315,6 +336,37 @@ void main() {
|
|||||||
expect(testBuildCalled, 2);
|
expect(testBuildCalled, 2);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
testWidgets('Text geometry set in Theme has higher precedence than that of Localizations', (WidgetTester tester) async {
|
||||||
|
const double _kMagicFontSize = 4321.0;
|
||||||
|
final ThemeData fallback = new ThemeData.fallback();
|
||||||
|
final ThemeData customTheme = fallback.copyWith(
|
||||||
|
primaryTextTheme: fallback.primaryTextTheme.copyWith(
|
||||||
|
body1: fallback.primaryTextTheme.body1.copyWith(
|
||||||
|
fontSize: _kMagicFontSize,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(customTheme.primaryTextTheme.body1.fontSize, _kMagicFontSize);
|
||||||
|
|
||||||
|
double actualFontSize;
|
||||||
|
await tester.pumpWidget(new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Theme(
|
||||||
|
data: customTheme,
|
||||||
|
child: new Builder(builder: (BuildContext context) {
|
||||||
|
final ThemeData theme = Theme.of(context);
|
||||||
|
actualFontSize = theme.primaryTextTheme.body1.fontSize;
|
||||||
|
return new Text(
|
||||||
|
'A',
|
||||||
|
style: theme.primaryTextTheme.body1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(actualFontSize, _kMagicFontSize);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int testBuildCalled;
|
int testBuildCalled;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user