introduce localized text geometry in MaterialLocalizations (#11829)
* introduce localized text geometry in MaterialLocalizations
* remove geometry from color text themes
* fix merge conflict
* optional Localizations
* fix fallback; test; docs
diff --git a/packages/flutter/lib/src/material/i18n/localizations.dart b/packages/flutter/lib/src/material/i18n/localizations.dart
index ce972ac..09ccef5 100644
--- a/packages/flutter/lib/src/material/i18n/localizations.dart
+++ b/packages/flutter/lib/src/material/i18n/localizations.dart
@@ -12,6 +12,7 @@
/// This variable is used by [MaterialLocalizations].
const Map<String, Map<String, String>> localizations = const <String, Map<String, String>> {
"ar": const <String, String>{
+ "scriptCategory": r"tall",
"timeOfDayFormat": r"h:mm a",
"openAppDrawerTooltip": r"افتح قائمة التنقل",
"backButtonTooltip": r"الى الخلف",
@@ -40,6 +41,7 @@
"postMeridiemAbbreviation": r"م",
},
"de": const <String, String>{
+ "scriptCategory": r"English-like",
"timeOfDayFormat": r"HH:mm",
"openAppDrawerTooltip": r"Navigationsmenü öffnen",
"backButtonTooltip": r"Zurück",
@@ -68,6 +70,7 @@
"viewLicensesButtonLabel": r"LIZENZEN ANZEIGEN",
},
"en": const <String, String>{
+ "scriptCategory": r"English-like",
"timeOfDayFormat": r"h:mm a",
"openAppDrawerTooltip": r"Open navigation menu",
"backButtonTooltip": r"Back",
@@ -107,6 +110,7 @@
"timeOfDayFormat": r"HH:mm",
},
"es": const <String, String>{
+ "scriptCategory": r"English-like",
"timeOfDayFormat": r"H:mm",
"openAppDrawerTooltip": r"Abrir el menú de navegación",
"backButtonTooltip": r"Espalda",
@@ -140,6 +144,7 @@
"postMeridiemAbbreviation": r"PM",
},
"fa": const <String, String>{
+ "scriptCategory": r"tall",
"timeOfDayFormat": r"H:mm",
"openAppDrawerTooltip": r"منوی ناوبری را باز کنید",
"backButtonTooltip": r"بازگشت",
@@ -166,6 +171,7 @@
"viewLicensesButtonLabel": r"مشاهده مجوز",
},
"fr": const <String, String>{
+ "scriptCategory": r"English-like",
"timeOfDayFormat": r"HH:mm",
"openAppDrawerTooltip": r"Ouvrir le menu de navigation",
"backButtonTooltip": r"Retour",
@@ -197,6 +203,7 @@
"timeOfDayFormat": r"HH 'h' mm",
},
"he": const <String, String>{
+ "scriptCategory": r"English-like",
"timeOfDayFormat": r"H:mm",
"openAppDrawerTooltip": r"פתח תפריט ניווט",
"backButtonTooltip": r"אחורה",
@@ -223,6 +230,7 @@
"viewLicensesButtonLabel": r"ראה רישיונות",
},
"it": const <String, String>{
+ "scriptCategory": r"English-like",
"timeOfDayFormat": r"HH:mm",
"openAppDrawerTooltip": r"Apri il menu di navigazione",
"backButtonTooltip": r"Indietro",
@@ -249,6 +257,7 @@
"viewLicensesButtonLabel": r"VEDI LE LICENZE",
},
"ja": const <String, String>{
+ "scriptCategory": r"dense",
"timeOfDayFormat": r"H:mm",
"openAppDrawerTooltip": r"ナビゲーションメニューを開く",
"backButtonTooltip": r"戻る",
@@ -275,6 +284,7 @@
"viewLicensesButtonLabel": r"ライセンス表記",
},
"ps": const <String, String>{
+ "scriptCategory": r"tall",
"timeOfDayFormat": r"HH:mm",
"openAppDrawerTooltip": r"د پرانیستی نیینګ مینو",
"backButtonTooltip": r"شاته",
@@ -301,6 +311,7 @@
"viewLicensesButtonLabel": r"لیدلس وګورئ",
},
"pt": const <String, String>{
+ "scriptCategory": r"English-like",
"timeOfDayFormat": r"HH:mm",
"openAppDrawerTooltip": r"Abrir menu de navegação",
"backButtonTooltip": r"Costas",
@@ -327,6 +338,7 @@
"viewLicensesButtonLabel": r"VER LICENÇAS",
},
"ru": const <String, String>{
+ "scriptCategory": r"English-like",
"timeOfDayFormat": r"H:mm",
"openAppDrawerTooltip": r"Открыть меню навигации",
"backButtonTooltip": r"Назад",
@@ -355,6 +367,7 @@
"viewLicensesButtonLabel": r"ПРОСМОТРЕТЬ ЛИЦЕНЗИИ",
},
"sd": const <String, String>{
+ "scriptCategory": r"tall",
"timeOfDayFormat": r"HH:mm",
"openAppDrawerTooltip": r"اوپن جي مينڊيٽ مينيو",
"backButtonTooltip": r"پوئتي",
@@ -381,6 +394,7 @@
"viewLicensesButtonLabel": r"لائسنس ڏسو",
},
"ur": const <String, String>{
+ "scriptCategory": r"tall",
"timeOfDayFormat": r"h:mm a",
"openAppDrawerTooltip": r"کھولیں نیویگیشن مینو",
"backButtonTooltip": r"واپس",
@@ -409,6 +423,7 @@
"postMeridiemAbbreviation": r"PM",
},
"zh": const <String, String>{
+ "scriptCategory": r"dense",
"timeOfDayFormat": r"ah:mm",
"openAppDrawerTooltip": r"打开导航菜单",
"backButtonTooltip": r"返回",
diff --git a/packages/flutter/lib/src/material/i18n/material_ar.arb b/packages/flutter/lib/src/material/i18n/material_ar.arb
index dd2b6fc..bc7acf2 100644
--- a/packages/flutter/lib/src/material/i18n/material_ar.arb
+++ b/packages/flutter/lib/src/material/i18n/material_ar.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "tall",
"timeOfDayFormat": "h:mm a",
"openAppDrawerTooltip": "افتح قائمة التنقل",
"backButtonTooltip": "الى الخلف",
diff --git a/packages/flutter/lib/src/material/i18n/material_de.arb b/packages/flutter/lib/src/material/i18n/material_de.arb
index 6d05c7e..d8742ee 100644
--- a/packages/flutter/lib/src/material/i18n/material_de.arb
+++ b/packages/flutter/lib/src/material/i18n/material_de.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "English-like",
"timeOfDayFormat": "HH:mm",
"@anteMeridiemAbbreviation": { "notUsed": "German time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "German time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_en.arb b/packages/flutter/lib/src/material/i18n/material_en.arb
index 6d7e42f..6a20572 100644
--- a/packages/flutter/lib/src/material/i18n/material_en.arb
+++ b/packages/flutter/lib/src/material/i18n/material_en.arb
@@ -1,4 +1,10 @@
{
+ "scriptCategory": "English-like",
+ "@scriptCategory": {
+ "description": "The name of the language's script category (see https://material.io/guidelines/style/typography.html#typography-language-categories-reference)",
+ "type": "text"
+ },
+
"timeOfDayFormat": "h:mm a",
"@timeOfDayFormat": {
"description": "The ICU 'Short Time' pattern, such as 'HH:mm', 'h:mm a', 'H:mm'. See: http://demo.icu-project.org/icu-bin/locexp?d_=en&_=en_US",
diff --git a/packages/flutter/lib/src/material/i18n/material_es.arb b/packages/flutter/lib/src/material/i18n/material_es.arb
index a6371ed..2031171 100644
--- a/packages/flutter/lib/src/material/i18n/material_es.arb
+++ b/packages/flutter/lib/src/material/i18n/material_es.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "English-like",
"timeOfDayFormat": "H:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Standard Spanish time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Standard Spanish time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_fa.arb b/packages/flutter/lib/src/material/i18n/material_fa.arb
index 799e727..008a70c 100644
--- a/packages/flutter/lib/src/material/i18n/material_fa.arb
+++ b/packages/flutter/lib/src/material/i18n/material_fa.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "tall",
"timeOfDayFormat": "H:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Farsi time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Farsi time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_fr.arb b/packages/flutter/lib/src/material/i18n/material_fr.arb
index 80c1766..96983cd 100644
--- a/packages/flutter/lib/src/material/i18n/material_fr.arb
+++ b/packages/flutter/lib/src/material/i18n/material_fr.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "English-like",
"timeOfDayFormat": "HH:mm",
"@anteMeridiemAbbreviation": { "notUsed": "French time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "French time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_he.arb b/packages/flutter/lib/src/material/i18n/material_he.arb
index da02eb7..6144417 100644
--- a/packages/flutter/lib/src/material/i18n/material_he.arb
+++ b/packages/flutter/lib/src/material/i18n/material_he.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "English-like",
"timeOfDayFormat": "H:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Hebrew time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Hebrew time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_it.arb b/packages/flutter/lib/src/material/i18n/material_it.arb
index 9ff289d..63b37cf 100644
--- a/packages/flutter/lib/src/material/i18n/material_it.arb
+++ b/packages/flutter/lib/src/material/i18n/material_it.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "English-like",
"timeOfDayFormat": "HH:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Italian time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Italian time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_ja.arb b/packages/flutter/lib/src/material/i18n/material_ja.arb
index 574f058..be58155 100644
--- a/packages/flutter/lib/src/material/i18n/material_ja.arb
+++ b/packages/flutter/lib/src/material/i18n/material_ja.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "dense",
"timeOfDayFormat": "H:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Japanese time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Japanese time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_ps.arb b/packages/flutter/lib/src/material/i18n/material_ps.arb
index f1e3d23..a6c66bc 100644
--- a/packages/flutter/lib/src/material/i18n/material_ps.arb
+++ b/packages/flutter/lib/src/material/i18n/material_ps.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "tall",
"timeOfDayFormat": "HH:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Pashto time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Pashto time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_pt.arb b/packages/flutter/lib/src/material/i18n/material_pt.arb
index 6f17d41..71df02a 100644
--- a/packages/flutter/lib/src/material/i18n/material_pt.arb
+++ b/packages/flutter/lib/src/material/i18n/material_pt.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "English-like",
"timeOfDayFormat": "HH:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Portuguese time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Portuguese time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_ru.arb b/packages/flutter/lib/src/material/i18n/material_ru.arb
index 985941e..fed999f 100644
--- a/packages/flutter/lib/src/material/i18n/material_ru.arb
+++ b/packages/flutter/lib/src/material/i18n/material_ru.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "English-like",
"timeOfDayFormat": "H:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Russian time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Russian time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_sd.arb b/packages/flutter/lib/src/material/i18n/material_sd.arb
index 09f560c..3d2c115 100644
--- a/packages/flutter/lib/src/material/i18n/material_sd.arb
+++ b/packages/flutter/lib/src/material/i18n/material_sd.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "tall",
"timeOfDayFormat": "HH:mm",
"@anteMeridiemAbbreviation": { "notUsed": "Sindhi time format does not use a.m. indicator" },
"@postMeridiemAbbreviation": { "notUsed": "Sindhi time format does not use p.m. indicator" },
diff --git a/packages/flutter/lib/src/material/i18n/material_ur.arb b/packages/flutter/lib/src/material/i18n/material_ur.arb
index aab25d9..042c79f 100644
--- a/packages/flutter/lib/src/material/i18n/material_ur.arb
+++ b/packages/flutter/lib/src/material/i18n/material_ur.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "tall",
"timeOfDayFormat": "h:mm a",
"openAppDrawerTooltip": "کھولیں نیویگیشن مینو",
"backButtonTooltip": "واپس",
diff --git a/packages/flutter/lib/src/material/i18n/material_zh.arb b/packages/flutter/lib/src/material/i18n/material_zh.arb
index 08a5f1a..764957e 100644
--- a/packages/flutter/lib/src/material/i18n/material_zh.arb
+++ b/packages/flutter/lib/src/material/i18n/material_zh.arb
@@ -1,4 +1,5 @@
{
+ "scriptCategory": "dense",
"timeOfDayFormat": "ah:mm",
"openAppDrawerTooltip": "打开导航菜单",
"backButtonTooltip": "返回",
diff --git a/packages/flutter/lib/src/material/material_localizations.dart b/packages/flutter/lib/src/material/material_localizations.dart
index 94f5efd..650cf1e 100644
--- a/packages/flutter/lib/src/material/material_localizations.dart
+++ b/packages/flutter/lib/src/material/material_localizations.dart
@@ -9,6 +9,7 @@
import 'package:intl/intl.dart' as intl;
import 'i18n/localizations.dart';
+import 'typography.dart';
/// Defines the localized resource values used by the Material widgets.
///
@@ -95,6 +96,19 @@
/// each supported layout.
TimeOfDayFormat get timeOfDayFormat;
+ /// 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. The text styles provided by this theme have
+ /// their [TextStyle.inherit] property set to `true`.
+ ///
+ /// Typically a complete theme is obtained via [Theme.of], which can be
+ /// localized using the [Localizations] widget.
+ ///
+ /// See also: https://material.io/guidelines/style/typography.html
+ TextTheme get localTextGeometry;
+
/// The `MaterialLocalizations` from the closest [Localizations] instance
/// that encloses the given context.
///
@@ -287,6 +301,10 @@
return _icuTimeOfDayToEnum[icuShortTimePattern];
}
+ /// Looks up text geometry defined in [MaterialTextGeometry].
+ @override
+ TextTheme get localTextGeometry => MaterialTextGeometry.forScriptCategory(_nameToValue["scriptCategory"]);
+
/// Creates an object that provides localized resource values for the
/// for the widgets of the material library.
///
diff --git a/packages/flutter/lib/src/material/theme.dart b/packages/flutter/lib/src/material/theme.dart
index 03c6b0d..6d64f10 100644
--- a/packages/flutter/lib/src/material/theme.dart
+++ b/packages/flutter/lib/src/material/theme.dart
@@ -5,7 +5,9 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
+import 'material_localizations.dart';
import 'theme_data.dart';
+import 'typography.dart';
export 'theme_data.dart' show Brightness, ThemeData;
@@ -65,6 +67,10 @@
/// The data from the closest [Theme] instance that encloses the given
/// context.
///
+ /// If the given context is enclosed in a [Localizations] widget providing
+ /// [MaterialLocalizations], the returned data is localized according to the
+ /// nearest available [MaterialLocalizations].
+ ///
/// Defaults to [new ThemeData.fallback] if there is no [Theme] in the given
/// build context.
///
@@ -123,7 +129,11 @@
return null;
return inheritedTheme.theme.data;
}
- return (inheritedTheme != null) ? inheritedTheme.theme.data : _kFallbackTheme;
+
+ 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);
}
@override
diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart
index 591e9ed..8ef8bcb 100644
--- a/packages/flutter/lib/src/material/theme_data.dart
+++ b/packages/flutter/lib/src/material/theme_data.dart
@@ -48,7 +48,6 @@
/// 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.
///
@@ -248,14 +247,26 @@
assert(platform != null);
/// A default light blue theme.
+ ///
+ /// This theme does not contain text geometry. Instead, it is expected that
+ /// this theme is localized using text geometry using [ThemeData.localize].
factory ThemeData.light() => new ThemeData(brightness: Brightness.light);
/// A default dark theme with a teal accent color.
+ ///
+ /// This theme does not contain text geometry. Instead, it is expected that
+ /// this theme is localized using text geometry using [ThemeData.localize].
factory ThemeData.dark() => new ThemeData(brightness: Brightness.dark);
- /// The default theme. Same as [new ThemeData.light].
+ /// The default color theme. Same as [new ThemeData.light].
///
/// This is used by [Theme.of] when no theme has been specified.
+ ///
+ /// This theme does not contain text geometry. Instead, it is expected that
+ /// this theme is localized using text geometry using [ThemeData.localize].
+ ///
+ /// Most applications would use [Theme.of], which provides correct localized
+ /// text geometry.
factory ThemeData.fallback() => new ThemeData.light();
/// The brightness of the overall theme of the application. Used by widgets
@@ -407,40 +418,52 @@
IconThemeData accentIconTheme,
TargetPlatform 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,
+ 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,
);
}
+ /// 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) {
+ assert(baseTheme != null);
+ assert(localTextGeometry != null);
+ return new _LocalizedThemeData(baseTheme, localTextGeometry);
+ }
+
// See <https://www.w3.org/TR/WCAG20/#relativeluminancedef>
static double _linearizeColorComponent(double component) {
if (component <= 0.03928)
@@ -590,3 +613,246 @@
@override
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);
+
+ final ThemeData delegate;
+ final TextTheme localTextGeometry;
+
+ @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;
+
+ @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].
+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,
+ );
+}
diff --git a/packages/flutter/lib/src/material/typography.dart b/packages/flutter/lib/src/material/typography.dart
index eb388a1..568fe0f 100644
--- a/packages/flutter/lib/src/material/typography.dart
+++ b/packages/flutter/lib/src/material/typography.dart
@@ -7,10 +7,6 @@
import 'colors.dart';
-// TODO(eseidel): Font weights are supposed to be language relative.
-// TODO(jackson): Baseline should be language relative.
-// TODO(ianh): These values are for English-like text.
-
/// Material design text theme.
///
/// Definitions for the various typographical styles found in material design
@@ -62,58 +58,6 @@
this.button,
});
- const TextTheme._blackMountainView()
- : display4 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 112.0, fontWeight: FontWeight.w100, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- display3 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- display2 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 45.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- display1 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 34.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- headline = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 24.0, fontWeight: FontWeight.w400, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- title = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 20.0, fontWeight: FontWeight.w500, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- subhead = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 16.0, fontWeight: FontWeight.w400, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- body2 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- body1 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w400, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- caption = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 12.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- button = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.black87, textBaseline: TextBaseline.alphabetic);
-
- const TextTheme._whiteMountainView()
- : display4 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 112.0, fontWeight: FontWeight.w100, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- display3 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- display2 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 45.0, fontWeight: FontWeight.w400, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- display1 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 34.0, fontWeight: FontWeight.w400, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- headline = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 24.0, fontWeight: FontWeight.w400, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- title = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 20.0, fontWeight: FontWeight.w500, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- subhead = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 16.0, fontWeight: FontWeight.w400, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- body2 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- body1 = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w400, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- caption = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 12.0, fontWeight: FontWeight.w400, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- button = const TextStyle(fontFamily: 'Roboto', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.white, textBaseline: TextBaseline.alphabetic);
-
- const TextTheme._blackCupertino()
- : display4 = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 112.0, fontWeight: FontWeight.w100, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- display3 = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- display2 = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 45.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- display1 = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 34.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- headline = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 24.0, fontWeight: FontWeight.w400, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- title = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 20.0, fontWeight: FontWeight.w500, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- subhead = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 16.0, fontWeight: FontWeight.w400, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- body2 = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- body1 = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w400, color: Colors.black87, textBaseline: TextBaseline.alphabetic),
- caption = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 12.0, fontWeight: FontWeight.w400, color: Colors.black54, textBaseline: TextBaseline.alphabetic),
- button = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.black87, textBaseline: TextBaseline.alphabetic);
-
- const TextTheme._whiteCupertino()
- : display4 = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 112.0, fontWeight: FontWeight.w100, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- display3 = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- display2 = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 45.0, fontWeight: FontWeight.w400, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- display1 = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 34.0, fontWeight: FontWeight.w400, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- headline = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 24.0, fontWeight: FontWeight.w400, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- title = const TextStyle(fontFamily: '.SF UI Display', inherit: false, fontSize: 20.0, fontWeight: FontWeight.w500, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- subhead = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 16.0, fontWeight: FontWeight.w400, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- body2 = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- body1 = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w400, color: Colors.white, textBaseline: TextBaseline.alphabetic),
- caption = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 12.0, fontWeight: FontWeight.w400, color: Colors.white70, textBaseline: TextBaseline.alphabetic),
- button = const TextStyle(fontFamily: '.SF UI Text', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.white, textBaseline: TextBaseline.alphabetic);
-
/// Extremely large text.
///
/// The font size is 112 pixels.
@@ -187,6 +131,24 @@
);
}
+ TextTheme merge(TextTheme other) {
+ if (other == null)
+ return this;
+ return copyWith(
+ display4: display4.merge(other.display4),
+ display3: display3.merge(other.display3),
+ display2: display2.merge(other.display2),
+ display1: display1.merge(other.display1),
+ headline: headline.merge(other.headline),
+ title: title.merge(other.title),
+ subhead: subhead.merge(other.subhead),
+ body2: body2.merge(other.body2),
+ body1: body1.merge(other.body1),
+ caption: caption.merge(other.caption),
+ button: button.merge(other.button),
+ );
+ }
+
/// Creates a copy of this text theme but with the given field replaced in
/// each of the individual text styles.
///
@@ -353,13 +315,13 @@
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return const Typography._(
- const TextTheme._blackMountainView(),
- const TextTheme._whiteMountainView(),
+ _MaterialTextColorThemes.blackMountainView,
+ _MaterialTextColorThemes.whiteMountainView,
);
case TargetPlatform.iOS:
return const Typography._(
- const TextTheme._blackCupertino(),
- const TextTheme._whiteCupertino(),
+ _MaterialTextColorThemes.blackCupertino,
+ _MaterialTextColorThemes.whiteCupertino,
);
}
return null;
@@ -373,3 +335,149 @@
/// A material design text theme with light glyphs.
final TextTheme white;
}
+
+/// Provides default text theme colors compliant with the Material Design
+/// specification.
+///
+/// The geometric font properties are missing in these color themes. App are
+/// expected to use [Theme.of] to get [TextTheme] objects fully populated with
+/// font properties.
+///
+/// See also: 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 {
+ 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),
+ );
+
+ 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),
+ );
+
+ 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),
+ );
+
+ 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),
+ );
+}
+
+/// Defines text geometries for the three language categories defined in
+/// https://material.io/guidelines/style/typography.html.
+class MaterialTextGeometry {
+ /// The name of the English-like script category.
+ static const String englishLikeCategory = 'English-like';
+
+ /// The name of the dense script category.
+ static const String denseCategory = 'dense';
+
+ /// The name of the tall script category.
+ static const String tallCategory = 'tall';
+
+ /// The mapping from script category names to text themes.
+ static const Map<String, TextTheme> _categoryToTextTheme = const <String, TextTheme>{
+ englishLikeCategory: englishLike,
+ denseCategory: dense,
+ tallCategory: tall,
+ };
+
+ /// Looks up text geometry corresponding to the given [scriptCategoryName].
+ ///
+ /// Most apps would not call this method directly, but rather call [Theme.of]
+ /// and use the [TextTheme] fields of the returned [ThemeData] object.
+ ///
+ /// [scriptCategoryName] must be one of [englishLikeCategory], [denseCategory]
+ /// and [tallCategory].
+ ///
+ /// See also:
+ ///
+ /// * [DefaultMaterialLocalizations.localTextGeometry], which uses this
+ /// method to look-up text geometry for the current locale.
+ static TextTheme forScriptCategory(String scriptCategoryName) => _categoryToTextTheme[scriptCategoryName];
+
+ /// Defines text geometry for English-like scripts, such as English, French, Russian, etc.
+ static const TextTheme englishLike = const TextTheme(
+ display4: const TextStyle(fontSize: 112.0, fontWeight: FontWeight.w100, textBaseline: TextBaseline.alphabetic),
+ display3: const TextStyle(fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ display2: const TextStyle(fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ display1: const TextStyle(fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ headline: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ title : const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic),
+ subhead : const TextStyle(fontSize: 16.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ body2 : const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic),
+ body1 : const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ caption : const TextStyle(fontSize: 12.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ button : const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic),
+ );
+
+ /// Defines text geometry for dense scripts, such as Chinese, Japanese, Korean, etc.
+ static const TextTheme dense = const TextTheme(
+ display4: const TextStyle(fontSize: 112.0, fontWeight: FontWeight.w100, textBaseline: TextBaseline.ideographic),
+ display3: const TextStyle(fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic),
+ display2: const TextStyle(fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic),
+ display1: const TextStyle(fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic),
+ headline: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic),
+ title : const TextStyle(fontSize: 21.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic),
+ subhead : const TextStyle(fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic),
+ body2 : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic),
+ body1 : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic),
+ caption : const TextStyle(fontSize: 13.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic),
+ button : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic),
+ );
+
+ /// Defines text geometry for tall scripts, such as Farsi, Hindi, Thai, etc.
+ static const TextTheme tall = const TextTheme(
+ display4: const TextStyle(fontSize: 112.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ display3: const TextStyle(fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ display2: const TextStyle(fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ display1: const TextStyle(fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ headline: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ title : const TextStyle(fontSize: 21.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic),
+ subhead : const TextStyle(fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ body2 : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic),
+ body1 : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ caption : const TextStyle(fontSize: 13.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic),
+ button : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic),
+ );
+}
diff --git a/packages/flutter/lib/src/widgets/localizations.dart b/packages/flutter/lib/src/widgets/localizations.dart
index 7ac42ac..7640302 100644
--- a/packages/flutter/lib/src/widgets/localizations.dart
+++ b/packages/flutter/lib/src/widgets/localizations.dart
@@ -401,10 +401,13 @@
return new List<LocalizationsDelegate<dynamic>>.from(scope.localizationsState.widget.delegates);
}
- /// Returns the 'type' localized resources for the widget tree that
- /// corresponds to [BuildContext] `context`.
+ /// Returns the localized resources object of the given `type` for the widget
+ /// tree that corresponds to the given `context`.
///
- /// This method is typically used by a static factory method on the 'type'
+ /// Returns `null` if no resources object of the given `type` exists within
+ /// the given `context`.
+ ///
+ /// This method is typically used by a static factory method on the `type`
/// class. For example Flutter's MaterialLocalizations class looks up Material
/// resources with a method defined like this:
///
@@ -417,8 +420,7 @@
assert(context != null);
assert(type != null);
final _LocalizationsScope scope = context.inheritFromWidgetOfExactType(_LocalizationsScope);
- assert(scope != null, 'a Localizations ancestor was not found');
- return scope.localizationsState.resourcesFor<T>(type);
+ return scope?.localizationsState?.resourcesFor<T>(type);
}
@override
diff --git a/packages/flutter/test/material/material_localizations_test.dart b/packages/flutter/test/material/material_localizations_test.dart
new file mode 100644
index 0000000..4452502
--- /dev/null
+++ b/packages/flutter/test/material/material_localizations_test.dart
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter/widgets.dart';
+
+void main() {
+ testWidgets('$MaterialLocalizations localizes text inside the tree', (WidgetTester tester) async {
+ await tester.pumpWidget(new MaterialApp(
+ home: new ListView(
+ children: <Widget>[
+ new LocalizationTracker(key: const ValueKey<String>('outer')),
+ new Localizations(
+ locale: const Locale('zh', 'CN'),
+ delegates: <LocalizationsDelegate<dynamic>>[
+ new _MaterialLocalizationsDelegate(
+ new DefaultMaterialLocalizations(const Locale('zh', 'CN')),
+ ),
+ const DefaultWidgetsLocalizationsDelegate(),
+ ],
+ child: new LocalizationTracker(key: const ValueKey<String>('inner')),
+ ),
+ ],
+ ),
+ ));
+
+ final LocalizationTrackerState outerTracker = tester.state(find.byKey(const ValueKey<String>('outer')));
+ expect(outerTracker.captionFontSize, 12.0);
+ final LocalizationTrackerState innerTracker = tester.state(find.byKey(const ValueKey<String>('inner')));
+ expect(innerTracker.captionFontSize, 13.0);
+ });
+}
+
+class LocalizationTracker extends StatefulWidget {
+ LocalizationTracker({Key key}) : super(key: key);
+
+ @override
+ State<StatefulWidget> createState() => new LocalizationTrackerState();
+}
+
+class LocalizationTrackerState extends State<LocalizationTracker> {
+ double captionFontSize;
+
+ @override
+ Widget build(BuildContext context) {
+ captionFontSize = Theme.of(context).textTheme.caption.fontSize;
+ return new Container();
+ }
+}
+
+// Same as _MaterialLocalizationsDelegate in widgets/app.dart
+class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
+ const _MaterialLocalizationsDelegate(this.localizations);
+
+ final MaterialLocalizations localizations;
+
+ @override
+ Future<MaterialLocalizations> load(Locale locale) {
+ return new SynchronousFuture<MaterialLocalizations>(localizations);
+ }
+
+ @override
+ bool shouldReload(_MaterialLocalizationsDelegate old) => false;
+}
+
+// Same as _WidgetsLocalizationsDelegate in widgets/app.dart
+class DefaultWidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
+ const DefaultWidgetsLocalizationsDelegate();
+
+ @override
+ Future<WidgetsLocalizations> load(Locale locale) {
+ return new SynchronousFuture<WidgetsLocalizations>(new DefaultWidgetsLocalizations(locale));
+ }
+
+ @override
+ bool shouldReload(DefaultWidgetsLocalizationsDelegate old) => false;
+}
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index df7c0f3..f515a9b 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -4,8 +4,8 @@
import 'dart:async';
-import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
@@ -789,8 +789,11 @@
});
testWidgets('TextField with default helperStyle', (WidgetTester tester) async {
- final ThemeData themeData = new ThemeData(
- hintColor: Colors.blue[500],
+ final ThemeData themeData = ThemeData.localize(
+ new ThemeData(
+ hintColor: Colors.blue[500],
+ ),
+ MaterialTextGeometry.forScriptCategory(MaterialTextGeometry.englishLikeCategory),
);
await tester.pumpWidget(
diff --git a/packages/flutter/test/material/theme_test.dart b/packages/flutter/test/material/theme_test.dart
index b934e6a..c6d30ea 100644
--- a/packages/flutter/test/material/theme_test.dart
+++ b/packages/flutter/test/material/theme_test.dart
@@ -53,7 +53,9 @@
)
);
- expect(Theme.of(capturedContext), equals(new ThemeData.fallback()));
+ final dynamic localizedTheme = Theme.of(capturedContext);
+ expect('${localizedTheme.runtimeType}', '_LocalizedThemeData');
+ expect(localizedTheme.delegate, equals(new ThemeData.fallback()));
expect(Theme.of(capturedContext, shadowThemeOnly: true), isNull);
});
diff --git a/packages/flutter/test/material/typography_test.dart b/packages/flutter/test/material/typography_test.dart
index a37cffb..216b105 100644
--- a/packages/flutter/test/material/typography_test.dart
+++ b/packages/flutter/test/material/typography_test.dart
@@ -30,23 +30,27 @@
test('Typography on iOS defaults to the correct SF font family based on size', () {
// Ref: https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/
- final Matcher hasCorrectFont = predicate((TextStyle s) {
- return s.fontFamily == (s.fontSize <= 19.0 ? '.SF UI Text' : '.SF UI Display');
- }, 'Uses SF Display font for font sizes over 19.0, otherwise SF Text font');
+ final Matcher isDisplayFont = predicate((TextStyle s) {
+ return s.fontFamily == '.SF UI Display';
+ }, 'Uses SF Display font');
+
+ final Matcher isTextFont = predicate((TextStyle s) {
+ return s.fontFamily == '.SF UI Text';
+ }, 'Uses SF Text font');
final Typography typography = new Typography(platform: TargetPlatform.iOS);
for (TextTheme textTheme in <TextTheme>[typography.black, typography.white]) {
- expect(textTheme.display4, hasCorrectFont);
- expect(textTheme.display3, hasCorrectFont);
- expect(textTheme.display2, hasCorrectFont);
- expect(textTheme.display1, hasCorrectFont);
- expect(textTheme.headline, hasCorrectFont);
- expect(textTheme.title, hasCorrectFont);
- expect(textTheme.subhead, hasCorrectFont);
- expect(textTheme.body2, hasCorrectFont);
- expect(textTheme.body1, hasCorrectFont);
- expect(textTheme.caption, hasCorrectFont);
- expect(textTheme.button, hasCorrectFont);
+ expect(textTheme.display4, isDisplayFont);
+ expect(textTheme.display3, isDisplayFont);
+ expect(textTheme.display2, isDisplayFont);
+ expect(textTheme.display1, isDisplayFont);
+ expect(textTheme.headline, isDisplayFont);
+ expect(textTheme.title, isDisplayFont);
+ expect(textTheme.subhead, isTextFont);
+ expect(textTheme.body2, isTextFont);
+ expect(textTheme.body1, isTextFont);
+ expect(textTheme.caption, isTextFont);
+ expect(textTheme.button, isTextFont);
}
});
}