typography.dart 16.4 KB
Newer Older
1 2 3 4 5
// Copyright 2015 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.


6
import 'package:flutter/foundation.dart';
7
import 'package:flutter/painting.dart';
8
import 'package:meta/meta.dart';
9 10

import 'colors.dart';
11

12 13
// TODO(eseidel): Font weights are supposed to be language relative.
// TODO(jackson): Baseline should be language relative.
14
// These values are for English-like text.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

/// Material design text theme.
///
/// Definitions for the various typographical styles found in material design
/// (e.g., headline, caption). Rather than creating a [TextTheme] directly,
/// you can obtain an instance as [Typography.black] or [Typography.white].
///
/// To obtain the current text theme, call [Theme.of] with the current
/// [BuildContext] and read the [ThemeData.textTheme] property.
///
/// See also:
///
///  * [Typography]
///  * [Theme]
///  * [ThemeData]
30
///  * <http://material.google.com/style/typography.html>
31
class TextTheme {
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
  /// Create a text theme that uses the given values.
  ///
  /// Rather than creating a new text theme, consider using [Typography.black]
  /// or [Typography.white], which implement the typography styles in the
  /// material design specification:
  ///
  /// <https://material.google.com/style/typography.html#typography-styles>
  ///
  /// If you do decide to create your own text theme, consider using one of
  /// those predefined themes as a starting point for [copyWith] or [apply].
  const TextTheme({
    this.display4,
    this.display3,
    this.display2,
    this.display1,
    this.headline,
    this.title,
    this.subhead,
    this.body2,
    this.body1,
    this.caption,
    this.button
  });
55

56
  const TextTheme._blackMountainView()
57 58 59 60 61 62 63 64 65 66 67
    : 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);
68

69
  const TextTheme._whiteMountainView()
70 71 72 73 74 75 76 77 78 79 80
    : 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);
81

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  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);

108 109 110
  /// Extremely large text.
  ///
  /// The font size is 112 pixels.
111
  final TextStyle display4;
112 113 114 115

  /// Very, very large text.
  ///
  /// Used for the date in [DatePicker].
116
  final TextStyle display3;
117 118

  /// Very large text.
119
  final TextStyle display2;
120 121

  /// Large text.
122
  final TextStyle display1;
123 124

  /// Used for large text in dialogs (e.g., the month and year in [DatePicker]).
125
  final TextStyle headline;
126 127

  /// Used for the primary text in app bars and dialogs (e.g., [AppBar.title] and [Dialog.title]).
128
  final TextStyle title;
129 130

  /// Used for the primary text in lists (e.g., [ListItem.title]).
131
  final TextStyle subhead;
132 133

  /// Used for emphasizing text that would otherwise be [body1].
134
  final TextStyle body2;
135 136

  /// Used for the default text style for [Material].
137
  final TextStyle body1;
138

Ian Hickson's avatar
Ian Hickson committed
139
  /// Used for auxillary text associated with images.
140
  final TextStyle caption;
141 142

  /// Used for text on [RaisedButton] and [FlatButton].
143 144
  final TextStyle button;

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
  /// Creates a copy of this text theme but with the given fields replaced with
  /// the new values.
  ///
  /// Consider using [Typography.black] or [Typography.white], which implement
  /// the typography styles in the material design specification, as a starting
  /// point.
  TextTheme copyWith({
    TextStyle display4,
    TextStyle display3,
    TextStyle display2,
    TextStyle display1,
    TextStyle headline,
    TextStyle title,
    TextStyle subhead,
    TextStyle body2,
    TextStyle body1,
    TextStyle caption,
    TextStyle button
  }) {
    return new TextTheme(
      display4: display4 ?? this.display4,
      display3: display3 ?? this.display3,
      display2: display2 ?? this.display2,
      display1: display1 ?? this.display1,
      headline: headline ?? this.headline,
      title: title ?? this.title,
      subhead: subhead ?? this.subhead,
      body2: body2 ?? this.body2,
      body1: body1 ?? this.body1,
      caption: caption ?? this.caption,
      button: button ?? this.button
    );
  }

  /// Creates a copy of this text theme but with the given field replaced in
  /// each of the individual text styles.
  ///
  /// The `displayColor` is applied to [display4], [display3], [display2],
  /// [display1], and [caption]. The `bodyColor` is applied to the remaining
  /// text styles.
  ///
  /// Consider using [Typography.black] or [Typography.white], which implement
  /// the typography styles in the material design specification, as a starting
  /// point.
  TextTheme apply({
    String fontFamily,
    double fontSizeFactor: 1.0,
    double fontSizeDelta: 0.0,
    Color displayColor,
    Color bodyColor
  }) {
    return new TextTheme(
      display4: display4.apply(
        color: displayColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      display3: display3.apply(
        color: displayColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      display2: display2.apply(
        color: displayColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      display1: display1.apply(
        color: displayColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      headline: headline.apply(
        color: bodyColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      title: title.apply(
        color: bodyColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      subhead: subhead.apply(
        color: bodyColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      body2: body2.apply(
        color: bodyColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      body1: body1.apply(
        color: bodyColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      caption: caption.apply(
        color: displayColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
      button: button.apply(
        color: bodyColor,
        fontFamily: fontFamily,
        fontSizeFactor: fontSizeFactor,
        fontSizeDelta: fontSizeDelta
      ),
    );
  }

266
  /// Linearly interpolate between two text themes.
267
  static TextTheme lerp(TextTheme begin, TextTheme end, double t) {
268 269 270 271 272 273 274 275 276 277 278 279
    return new TextTheme(
      display4: TextStyle.lerp(begin.display4, end.display4, t),
      display3: TextStyle.lerp(begin.display3, end.display3, t),
      display2: TextStyle.lerp(begin.display2, end.display2, t),
      display1: TextStyle.lerp(begin.display1, end.display1, t),
      headline: TextStyle.lerp(begin.headline, end.headline, t),
      title: TextStyle.lerp(begin.title, end.title, t),
      subhead: TextStyle.lerp(begin.subhead, end.subhead, t),
      body2: TextStyle.lerp(begin.body2, end.body2, t),
      body1: TextStyle.lerp(begin.body1, end.body1, t),
      caption: TextStyle.lerp(begin.caption, end.caption, t),
      button: TextStyle.lerp(begin.button, end.button, t)
280 281
    );
  }
282
}
283

284 285
/// The two material design text themes.
///
286 287 288 289
/// Material design defines two text themes: [black] and [white]. The black
/// text theme, which uses dark glyphs, is used on light backgrounds in light
/// themes. The white text theme, which uses light glyphs, is used in dark
/// themes and on dark backgrounds in light themes.
290 291 292 293 294 295 296 297
///
/// To obtain the current text theme, call [Theme.of] with the current
/// [BuildContext] and read the [ThemeData.textTheme] property.
///
/// See also:
///
///  * [Theme]
///  * [ThemeData]
298
///  * <http://material.google.com/style/typography.html>
299
class Typography {
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
  /// Creates the default typography for the specified platform.
  factory Typography({ @required TargetPlatform platform }) {
    assert(platform != null);
    switch (platform) {
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
        return const Typography._(
            const TextTheme._blackMountainView(),
            const TextTheme._whiteMountainView(),
        );
      case TargetPlatform.iOS:
        return const Typography._(
            const TextTheme._blackCupertino(),
            const TextTheme._whiteCupertino(),
        );
    }
    return null;
  }

  const Typography._(this.black, this.white);
320

321
  /// A material design text theme with dark glyphs.
322
  final TextTheme black;
323

324
  /// A material design text theme with light glyphs.
325
  final TextTheme white;
326
}