Unverified Commit 8bfb4b3e authored by Hans Muller's avatar Hans Muller Committed by GitHub

Bring TextTheme into alignment with the current Material spec (#22330)

parent 87f97ca3
......@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This program generates a Dart "localizations" Map definition that combines
// the contents of the arb files. The map can be used to lookup a localized
// string: `localizations[localeString][resourceId]`.
// This program generates a getTranslation() function that looks up the
// translations contained by the arb files. The returned value is an
// instance of GlobalMaterialLocalizations that corresponds to a single
// locale.
//
// The *.arb files are in packages/flutter_localizations/lib/src/l10n.
//
......@@ -273,6 +274,8 @@ String generateType(Map<String, dynamic> attributes) {
switch (attributes['x-flutter-type']) {
case 'icuShortTimePattern':
return 'TimeOfDayFormat';
case 'scriptCategory':
return 'ScriptCategory';
}
}
return 'String';
......@@ -308,6 +311,12 @@ const Map<String, String> _icuTimeOfDayToEnum = <String, String>{
'ah:mm': 'TimeOfDayFormat.a_space_h_colon_mm',
};
const Map<String, String> _scriptCategoryToEnum = <String, String>{
'English-like': 'ScriptCategory.englishLike',
'dense': 'ScriptCategory.dense',
'tall': 'ScriptCategory.tall',
};
/// Returns the literal that describes the value returned by getters
/// with the given attributes.
///
......@@ -330,6 +339,15 @@ String generateValue(String value, Map<String, dynamic> attributes) {
);
}
return _icuTimeOfDayToEnum[value];
case 'scriptCategory':
if (!_scriptCategoryToEnum.containsKey(value)) {
throw Exception(
'"$value" is not one of the scriptCategory values supported '
'by the material library. Here is the list of supported '
'values:\n ' + _scriptCategoryToEnum.keys.join('\n ')
);
}
return _scriptCategoryToEnum[value];
}
}
return generateString(value);
......
......@@ -57,7 +57,7 @@ class TypographyDemo extends StatelessWidget {
TextStyleItem(name: 'Body 2', style: textTheme.body2, text: 'Medium 14sp'),
TextStyleItem(name: 'Body 1', style: textTheme.body1, text: 'Regular 14sp'),
TextStyleItem(name: 'Caption', style: textTheme.caption, text: 'Regular 12sp'),
TextStyleItem(name: 'Button', style: textTheme.button, text: 'MEDIUM (ALL CAPS) 14sp')
TextStyleItem(name: 'Button', style: textTheme.button, text: 'MEDIUM (ALL CAPS) 14sp'),
];
if (MediaQuery.of(context).size.width > 500.0) {
......
......@@ -97,6 +97,7 @@ export 'src/material/tabs.dart';
export 'src/material/text_field.dart';
export 'src/material/text_form_field.dart';
export 'src/material/text_selection.dart';
export 'src/material/text_theme.dart';
export 'src/material/theme.dart';
export 'src/material/theme_data.dart';
export 'src/material/time.dart';
......
......@@ -19,8 +19,8 @@ import 'material.dart';
import 'material_localizations.dart';
import 'scaffold.dart';
import 'tabs.dart';
import 'text_theme.dart';
import 'theme.dart';
import 'typography.dart';
// Examples can assume:
// void _airDress() { }
......
......@@ -14,8 +14,8 @@ import 'debug.dart';
import 'ink_well.dart';
import 'material.dart';
import 'material_localizations.dart';
import 'text_theme.dart';
import 'theme.dart';
import 'typography.dart';
const double _kActiveFontSize = 14.0;
const double _kInactiveFontSize = 12.0;
......
......@@ -21,8 +21,8 @@ import 'icons.dart';
import 'ink_well.dart';
import 'material.dart';
import 'material_localizations.dart';
import 'text_theme.dart';
import 'theme.dart';
import 'typography.dart';
/// Initial display mode of the date picker dialog.
///
......
......@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'text_theme.dart';
import 'time.dart';
import 'typography.dart';
......@@ -34,6 +35,10 @@ import 'typography.dart';
//
// 5. If you are a Google employee, you should then also follow the instructions
// at go/flutter-l10n. If you're not, don't worry about it.
//
// 6. If you're adding a String for the sake of Flutter, not for an app-specific
// version of this interface, you are making a breaking API change. See
// https://flutter.io/design-principles/#handling-breaking-changes.
/// Defines the localized resource values used by the Material widgets.
///
......@@ -168,22 +173,17 @@ abstract class MaterialLocalizations {
/// each supported layout.
TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat = false });
/// Provides geometric text preferences for the current locale.
///
/// This text theme is incomplete. For example, it lacks text color
/// information. This theme must be merged with another text theme that
/// provides the missing values.
/// Defines the localized [TextStyle] geometry for [ThemeData.textTheme].
///
/// Typically a complete theme is obtained via [Theme.of], which can be
/// localized using the [Localizations] widget.
/// The [scriptCategory] defines the overall geometry of a [TextTheme] for
/// the static [MaterialTextGeometry.localizedFor] method in terms of the
/// three language categories defined in https://material.io/go/design-typography.
///
/// The text styles provided by this theme are expected to have their
/// [TextStyle.inherit] property set to false, so that the [ThemeData]
/// obtained from [Theme.of] no longer inherits text style properties and
/// contains a complete set of properties needed to style a [Text] widget.
///
/// See also: https://material.io/go/design-typography
TextTheme get localTextGeometry;
/// Generally speaking, font sizes for [ScriptCategory.tall] and
/// [ScriptCategory.dense] scripts - for text styles that are smaller than the
/// title style - are one unit larger than they are for
/// [ScriptCategory.englishLike] scripts.
ScriptCategory get scriptCategory;
/// Formats [number] as a decimal, inserting locale-appropriate thousands
/// separators as necessary.
......@@ -652,6 +652,9 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
@override
String get modalBarrierDismissLabel => 'Dismiss';
@override
ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override
TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat = false }) {
return alwaysUse24HourFormat
......@@ -659,10 +662,6 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
: TimeOfDayFormat.h_colon_mm_space_a;
}
/// Looks up text geometry defined in [MaterialTextGeometry].
@override
TextTheme get localTextGeometry => MaterialTextGeometry.englishLike;
@override
String get signedInLabel => 'Signed in';
......
......@@ -12,8 +12,8 @@ import 'icons.dart';
import 'ink_well.dart';
import 'material.dart';
import 'material_localizations.dart';
import 'text_theme.dart';
import 'theme.dart';
import 'typography.dart';
// TODO(dragostis): Missing functionality:
// * mobile horizontal mode with adding/removing steps
......
This diff is collapsed.
......@@ -124,18 +124,17 @@ class Theme extends StatelessWidget {
/// }
/// ```
static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {
final _InheritedTheme inheritedTheme =
context.inheritFromWidgetOfExactType(_InheritedTheme);
final _InheritedTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedTheme);
if (shadowThemeOnly) {
if (inheritedTheme == null || inheritedTheme.theme.isMaterialAppTheme)
return null;
return inheritedTheme.theme.data;
}
final ThemeData colorTheme = (inheritedTheme != null) ? inheritedTheme.theme.data : _kFallbackTheme;
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final TextTheme geometryTheme = localizations?.localTextGeometry ?? MaterialTextGeometry.englishLike;
return ThemeData.localize(colorTheme, geometryTheme);
final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike;
final ThemeData theme = inheritedTheme?.theme?.data ?? _kFallbackTheme;
return ThemeData.localize(theme, theme.typography.geometryThemeFor(category));
}
@override
......
......@@ -18,6 +18,7 @@ import 'input_decorator.dart';
import 'page_transitions_theme.dart';
import 'slider_theme.dart';
import 'tab_bar_theme.dart';
import 'text_theme.dart';
import 'typography.dart';
export 'package:flutter/services.dart' show Brightness;
......@@ -149,6 +150,7 @@ class ThemeData extends Diagnosticable {
MaterialTapTargetSize materialTapTargetSize,
PageTransitionsTheme pageTransitionsTheme,
ColorScheme colorScheme,
Typography typography,
}) {
brightness ??= Brightness.light;
final bool isDark = brightness == Brightness.dark;
......@@ -200,7 +202,7 @@ class ThemeData extends Diagnosticable {
accentIconTheme ??= accentIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
iconTheme ??= isDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black87);
platform ??= defaultTargetPlatform;
final Typography typography = Typography(platform: platform);
typography ??= Typography(platform: platform);
final TextTheme defaultTextTheme = isDark ? typography.white : typography.black;
textTheme = defaultTextTheme.merge(textTheme);
final TextTheme defaultPrimaryTextTheme = primaryIsDark ? typography.white : typography.black;
......@@ -287,6 +289,7 @@ class ThemeData extends Diagnosticable {
materialTapTargetSize: materialTapTargetSize,
pageTransitionsTheme: pageTransitionsTheme,
colorScheme: colorScheme,
typography: typography,
);
}
......@@ -344,6 +347,7 @@ class ThemeData extends Diagnosticable {
@required this.materialTapTargetSize,
@required this.pageTransitionsTheme,
@required this.colorScheme,
@required this.typography,
}) : assert(brightness != null),
assert(primaryColor != null),
assert(primaryColorBrightness != null),
......@@ -386,7 +390,8 @@ class ThemeData extends Diagnosticable {
assert(platform != null),
assert(materialTapTargetSize != null),
assert(pageTransitionsTheme != null),
assert(colorScheme != null);
assert(colorScheme != null),
assert(typography != null);
// Warning: make sure these properties are in the exact same order as in
// hashValues() and in the raw constructor and in the order of fields in
......@@ -612,6 +617,10 @@ class ThemeData extends Diagnosticable {
/// that is possible without significant backwards compatibility breaks.
final ColorScheme colorScheme;
/// The color and geometry [TextTheme] values used to configure [textTheme],
/// [primaryTextTheme], and [accentTextTheme].
final Typography typography;
/// Creates a copy of this theme but with the given fields replaced with the new values.
ThemeData copyWith({
Brightness brightness,
......@@ -658,6 +667,7 @@ class ThemeData extends Diagnosticable {
MaterialTapTargetSize materialTapTargetSize,
PageTransitionsTheme pageTransitionsTheme,
ColorScheme colorScheme,
Typography typography,
}) {
return ThemeData.raw(
brightness: brightness ?? this.brightness,
......@@ -704,6 +714,7 @@ class ThemeData extends Diagnosticable {
materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
pageTransitionsTheme: pageTransitionsTheme ?? this.pageTransitionsTheme,
colorScheme: colorScheme ?? this.colorScheme,
typography: typography ?? this.typography,
);
}
......@@ -829,6 +840,7 @@ class ThemeData extends Diagnosticable {
materialTapTargetSize: t < 0.5 ? a.materialTapTargetSize : b.materialTapTargetSize,
pageTransitionsTheme: t < 0.5 ? a.pageTransitionsTheme : b.pageTransitionsTheme,
colorScheme: ColorScheme.lerp(a.colorScheme, b.colorScheme, t),
typography: Typography.lerp(a.typography, b.typography, t),
);
}
......@@ -883,7 +895,8 @@ class ThemeData extends Diagnosticable {
(otherData.platform == platform) &&
(otherData.materialTapTargetSize == materialTapTargetSize) &&
(otherData.pageTransitionsTheme == pageTransitionsTheme) &&
(otherData.colorScheme == colorScheme);
(otherData.colorScheme == colorScheme) &&
(otherData.typography == typography);
}
@override
......@@ -939,6 +952,7 @@ class ThemeData extends Diagnosticable {
materialTapTargetSize,
pageTransitionsTheme,
colorScheme,
typography,
),
),
);
......@@ -989,6 +1003,7 @@ class ThemeData extends Diagnosticable {
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize));
properties.add(DiagnosticsProperty<PageTransitionsTheme>('pageTransitionsTheme', pageTransitionsTheme));
properties.add(DiagnosticsProperty<ColorScheme>('colorScheme', colorScheme, defaultValue: defaultData.colorScheme));
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography));
}
}
......
......@@ -16,10 +16,10 @@ import 'dialog.dart';
import 'feedback.dart';
import 'flat_button.dart';
import 'material_localizations.dart';
import 'text_theme.dart';
import 'theme.dart';
import 'theme_data.dart';
import 'time.dart';
import 'typography.dart';
const Duration _kDialAnimateDuration = Duration(milliseconds: 200);
const double _kTwoPi = 2 * math.pi;
......
......@@ -915,7 +915,7 @@ void main() {
);
final Text helperText = tester.widget(find.text('helper text'));
expect(helperText.style.color, themeData.hintColor);
expect(helperText.style.fontSize, MaterialTextGeometry.englishLike.caption.fontSize);
expect(helperText.style.fontSize, Typography.englishLike2014.caption.fontSize);
});
testWidgets('TextField with specified helperStyle', (WidgetTester tester) async {
......
......@@ -9,6 +9,8 @@ import 'package:flutter/src/foundation/diagnostics.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
const TextTheme defaultGeometryTheme = Typography.englishLike2014;
test('ThemeDataTween control test', () {
final ThemeData light = ThemeData.light();
final ThemeData dark = ThemeData.dark();
......@@ -55,7 +57,7 @@ void main() {
)
);
expect(Theme.of(capturedContext), equals(ThemeData.localize(ThemeData.fallback(), MaterialTextGeometry.englishLike)));
expect(Theme.of(capturedContext), equals(ThemeData.localize(ThemeData.fallback(), defaultGeometryTheme)));
expect(Theme.of(capturedContext, shadowThemeOnly: true), isNull);
});
......@@ -65,20 +67,20 @@ void main() {
// Same input, same output.
expect(
ThemeData.localize(light, MaterialTextGeometry.englishLike),
same(ThemeData.localize(light, MaterialTextGeometry.englishLike)),
ThemeData.localize(light, defaultGeometryTheme),
same(ThemeData.localize(light, defaultGeometryTheme)),
);
// Different text geometry, different output.
expect(
ThemeData.localize(light, MaterialTextGeometry.englishLike),
isNot(same(ThemeData.localize(light, MaterialTextGeometry.tall))),
ThemeData.localize(light, defaultGeometryTheme),
isNot(same(ThemeData.localize(light, Typography.tall2014))),
);
// Different base theme, different output.
expect(
ThemeData.localize(light, MaterialTextGeometry.englishLike),
isNot(same(ThemeData.localize(dark, MaterialTextGeometry.englishLike))),
ThemeData.localize(light, defaultGeometryTheme),
isNot(same(ThemeData.localize(dark, defaultGeometryTheme))),
);
});
......@@ -408,7 +410,7 @@ void main() {
}
}
expect(theme.textTheme.display4.debugLabel, '(englishLike display4).merge(blackMountainView display4)');
expect(theme.textTheme.display4.debugLabel, '(englishLike display4 2014).merge(blackMountainView display4)');
});
}
......
......@@ -74,6 +74,8 @@ void main() {
expect(textTheme.body1, isTextFont);
expect(textTheme.caption, isTextFont);
expect(textTheme.button, isTextFont);
expect(textTheme.subtitle, isTextFont);
expect(textTheme.overline, isTextFont);
}
});
}
......@@ -138,11 +138,11 @@ It is converted to an enum value because the `material_en.arb` file
has this value labeled as `"x-flutter-type": "icuShortTimePattern"`.
The value of `scriptCategory` is based on the
[Language categories reference](https://material.io/go/design-typography#typography-language-categories-reference)
[Language categories reference](https://material.io/design/typography/language-support.html#language-categories-reference)
section in the Material spec. The `scriptCategory` value is used when looking up
the `TextTheme`, see the
[MaterialTextGeometry](https://docs.flutter.io/flutter/material/MaterialTextGeometry/forScriptCategory.html)
class.
[MaterialTextGeometry](https://docs.flutter.io/flutter/material/MaterialTextGeometry/localizedFor.html)
method.
### Generated file localizations.dart: all of the localizations as a Map
......
{
"scriptCategory": "English-like",
"@scriptCategory": {
"description": "The name of the language's script category (see https://material.io/go/design-typography#typography-language-categories-reference)."
"description": "The name of the language's script category (see https://material.io/design/typography/language-support.html#language-categories-reference).",
"x-flutter-type": "scriptCategory"
},
"timeOfDayFormat": "h:mm a",
......
......@@ -502,18 +502,8 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
).replaceFirst(r'$remainingCount', formatDecimal(remainingCount));
}
/// The script category used by [localTextGeometry]. Must be one of the strings
/// declared in [MaterialTextGeometry].
//
// TODO(ianh): make this return a TextTheme from MaterialTextGeometry.
// TODO(ianh): drop the constructor on MaterialTextGeometry.
// TODO(ianh): drop the strings on MaterialTextGeometry.
@protected
String get scriptCategory;
/// Looks up text geometry defined in [MaterialTextGeometry].
@override
TextTheme get localTextGeometry => MaterialTextGeometry.forScriptCategory(scriptCategory);
ScriptCategory get scriptCategory;
/// A [LocalizationsDelegate] that uses [GlobalMaterialLocalizations.load]
/// to create an instance of this class.
......
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