Commit cc3f5767 authored by Yegor's avatar Yegor Committed by GitHub

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
parent 0189696e
......@@ -48,6 +48,7 @@ const Color _kDarkThemeSplashColor = const Color(0x40CCCCCC);
/// Use this class to configure a [Theme] widget.
///
/// To obtain the current theme, use [Theme.of].
@immutable
class ThemeData {
/// Create a ThemeData given a set of preferred values.
///
......@@ -418,50 +419,80 @@ class ThemeData {
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,
return new ThemeData.raw(
brightness: brightness ?? this.brightness,
primaryColor: primaryColor ?? this.primaryColor,
primaryColorBrightness: primaryColorBrightness ?? this.primaryColorBrightness,
accentColor: accentColor ?? this.accentColor,
accentColorBrightness: accentColorBrightness ?? this.accentColorBrightness,
canvasColor: canvasColor ?? this.canvasColor,
scaffoldBackgroundColor: scaffoldBackgroundColor ?? this.scaffoldBackgroundColor,
cardColor: cardColor ?? this.cardColor,
dividerColor: dividerColor ?? this.dividerColor,
highlightColor: highlightColor ?? this.highlightColor,
splashColor: splashColor ?? this.splashColor,
selectedRowColor: selectedRowColor ?? this.selectedRowColor,
unselectedWidgetColor: unselectedWidgetColor ?? this.unselectedWidgetColor,
disabledColor: disabledColor ?? this.disabledColor,
buttonColor: buttonColor ?? this.buttonColor,
secondaryHeaderColor: secondaryHeaderColor ?? this.secondaryHeaderColor,
textSelectionColor: textSelectionColor ?? this.textSelectionColor,
textSelectionHandleColor: textSelectionHandleColor ?? this.textSelectionHandleColor,
backgroundColor: backgroundColor ?? this.backgroundColor,
dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor,
indicatorColor: indicatorColor ?? this.indicatorColor,
hintColor: hintColor ?? this.hintColor,
errorColor: errorColor ?? this.errorColor,
textTheme: textTheme ?? this.textTheme,
primaryTextTheme: primaryTextTheme ?? this.primaryTextTheme,
accentTextTheme: accentTextTheme ?? this.accentTextTheme,
iconTheme: iconTheme ?? this.iconTheme,
primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme,
accentIconTheme: accentIconTheme ?? this.accentIconTheme,
platform: platform ?? this.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
/// provided by the [localTextGeometry].
///
/// The [TextStyle.inherit] field in the text styles provided by
/// [localTextGeometry] must be set to true.
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(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>
......@@ -614,245 +645,58 @@ class ThemeData {
String toString() => '$runtimeType(${ platform != defaultTargetPlatform ? "$platform " : ''}$brightness $primaryColor etc...)';
}
/// A lazily evaluated theme that provides the properties of the given
/// [delegate] theme localized using the properties of the given
/// [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);
class _IdentityThemeDataCacheKey {
_IdentityThemeDataCacheKey(this.baseTheme, this.localTextGeometry);
final ThemeData delegate;
final ThemeData baseTheme;
final TextTheme localTextGeometry;
// Using XOR to make the hash function as fast as possible (e.g. Jenkins is
// noticeably slower).
@override
Color get accentColor => delegate.accentColor;
@override
Brightness get accentColorBrightness => delegate.accentColorBrightness;
@override
IconThemeData get accentIconTheme => delegate.accentIconTheme;
@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;
int get hashCode => identityHashCode(baseTheme) ^ identityHashCode(localTextGeometry);
@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,
);
bool operator ==(Object other) {
// We are explicitly ignoring the possibility that the types might not
// match in the interests of speed.
final _IdentityThemeDataCacheKey otherKey = other;
return identical(baseTheme, otherKey.baseTheme) && identical(localTextGeometry, otherKey.localTextGeometry);
}
}
/// Implementation of [ThemeData.copyWith], shared with [_LocalizedThemeData.copyWith].
ThemeData _copyThemeDataWith(
ThemeData base, {
@required Brightness brightness,
@required Color primaryColor,
@required Brightness primaryColorBrightness,
@required Color accentColor,
@required Brightness accentColorBrightness,
@required Color canvasColor,
@required Color scaffoldBackgroundColor,
@required Color cardColor,
@required Color dividerColor,
@required Color highlightColor,
@required Color splashColor,
@required Color selectedRowColor,
@required Color unselectedWidgetColor,
@required Color disabledColor,
@required Color buttonColor,
@required Color secondaryHeaderColor,
@required Color textSelectionColor,
@required Color textSelectionHandleColor,
@required Color backgroundColor,
@required Color dialogBackgroundColor,
@required Color indicatorColor,
@required Color hintColor,
@required Color errorColor,
@required TextTheme textTheme,
@required TextTheme primaryTextTheme,
@required TextTheme accentTextTheme,
@required IconThemeData iconTheme,
@required IconThemeData primaryIconTheme,
@required IconThemeData accentIconTheme,
@required TargetPlatform platform,
}) {
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,
);
/// Cache of objects of limited size that uses the first in first out eviction
/// strategy (a.k.a least recently inserted).
///
/// The key that was inserted before all other keys is evicted first, i.e. the
/// one inserted least recently.
class _FifoCache<K, V> {
_FifoCache(this._maximumSize)
: assert(_maximumSize != null && _maximumSize > 0);
/// In Dart the map literal uses a linked hash-map implementation, whose keys
/// are stored such that [Map.keys] returns them in the order they were
/// inserted.
final Map<K, V> _cache = <K, V>{};
/// Maximum number of entries to store in the cache.
///
/// Once this many entries have been cached, the entry inserted least recently
/// is evicted when adding a new entry.
final int _maximumSize;
/// Returns the previously cached value for the given key, if available;
/// if not, calls the given callback to obtain it first.
///
/// The arguments must not be null.
V putIfAbsent(K key, V loader()) {
assert(key != null);
assert(loader != null);
final V result = _cache[key];
if (result != null)
return result;
if (_cache.length == _maximumSize)
_cache.remove(_cache.keys.first);
return _cache[key] = loader();
}
}
......@@ -353,59 +353,59 @@ class Typography {
// TODO(yjbanov): implement font fallback (see "Font stack" at https://material.io/guidelines/style/typography.html)
class _MaterialTextColorThemes {
static const TextTheme blackMountainView = const TextTheme(
display4: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
display3: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
display2: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
display1: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
headline: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
title : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
subhead : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
body2 : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
body1 : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
caption : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black54),
button : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.black87),
display4: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
display3: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
display2: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
display1: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
headline: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
title : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
subhead : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
body2 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
body1 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
caption : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54),
button : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87),
);
static const TextTheme whiteMountainView = const TextTheme(
display4: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
display3: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
display2: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
display1: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
headline: const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
title : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
subhead : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
body2 : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
body1 : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
caption : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white70),
button : const TextStyle(fontFamily: 'Roboto', inherit: false, color: Colors.white),
display4: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
display3: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
display2: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
display1: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
headline: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
title : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
subhead : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
body2 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
body1 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
caption : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70),
button : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white),
);
static const TextTheme blackCupertino = const TextTheme(
display4: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black54),
display3: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black54),
display2: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black54),
display1: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black54),
headline: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black87),
title : const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.black87),
subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black87),
body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black87),
body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black87),
caption : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black54),
button : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.black87),
display4: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54),
display3: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54),
display2: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54),
display1: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54),
headline: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black87),
title : const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black87),
subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87),
body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87),
body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87),
caption : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black54),
button : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87),
);
static const TextTheme whiteCupertino = const TextTheme(
display4: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white70),
display3: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white70),
display2: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white70),
display1: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white70),
headline: const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white),
title : const TextStyle(fontFamily: '.SF UI Display', inherit: false, color: Colors.white),
subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white),
body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white),
body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white),
caption : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white70),
button : const TextStyle(fontFamily: '.SF UI Text', inherit: false, color: Colors.white),
display4: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70),
display3: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70),
display2: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70),
display1: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70),
headline: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white),
title : const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white),
subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white),
body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white),
body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white),
caption : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white70),
button : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white),
);
}
......
......@@ -3911,9 +3911,9 @@ class InheritedElement extends ProxyElement {
assert(_active);
final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = new Map<Type, InheritedElement>.from(incomingWidgets);
_inheritedWidgets = new HashMap<Type, InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = <Type, InheritedElement>{};
_inheritedWidgets = new HashMap<Type, InheritedElement>();
_inheritedWidgets[widget.runtimeType] = this;
}
......
......@@ -53,12 +53,33 @@ void main() {
)
);
final dynamic localizedTheme = Theme.of(capturedContext);
expect('${localizedTheme.runtimeType}', '_LocalizedThemeData');
expect(localizedTheme.delegate, equals(new ThemeData.fallback()));
expect(Theme.of(capturedContext), equals(ThemeData.localize(new ThemeData.fallback(), MaterialTextGeometry.englishLike)));
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 {
// Regression test for https://github.com/flutter/flutter/issues/5572
final Key popupMenuButtonKey = new UniqueKey();
......@@ -315,6 +336,37 @@ void main() {
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;
......
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