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);
     }
   });
 }