| 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | /// @docImport 'package:flutter/material.dart'; |
| 6 | library; |
| 7 | |
| 8 | import 'dart:collection'; |
| 9 | import 'dart:ui' |
| 10 | as ui |
| 11 | show ParagraphStyle, Shadow, StrutStyle, TextStyle, kTextHeightNone, lerpDouble; |
| 12 | |
| 13 | import 'package:flutter/foundation.dart'; |
| 14 | |
| 15 | import 'basic_types.dart'; |
| 16 | import 'colors.dart'; |
| 17 | import 'strut_style.dart'; |
| 18 | import 'text_painter.dart'; |
| 19 | import 'text_scaler.dart'; |
| 20 | |
| 21 | const String _kDefaultDebugLabel = 'unknown' ; |
| 22 | |
| 23 | const String _kColorForegroundWarning = |
| 24 | 'Cannot provide both a color and a foreground\n' |
| 25 | 'The color argument is just a shorthand for "foreground: Paint()..color = color".' ; |
| 26 | |
| 27 | const String _kColorBackgroundWarning = |
| 28 | 'Cannot provide both a backgroundColor and a background\n' |
| 29 | 'The backgroundColor argument is just a shorthand for "background: Paint()..color = color".' ; |
| 30 | |
| 31 | // Examples can assume: |
| 32 | // late BuildContext context; |
| 33 | |
| 34 | /// An immutable style describing how to format and paint text. |
| 35 | /// |
| 36 | /// {@youtube 560 315 https://www.youtube.com/watch?v=1z6YP7YmvwA} |
| 37 | /// |
| 38 | /// ### Bold |
| 39 | /// |
| 40 | /// {@tool snippet} |
| 41 | /// Here, a single line of text in a [Text] widget is given a specific style |
| 42 | /// override. The style is mixed with the ambient [DefaultTextStyle] by the |
| 43 | /// [Text] widget. |
| 44 | /// |
| 45 | ///  |
| 46 | /// |
| 47 | /// ```dart |
| 48 | /// const Text( |
| 49 | /// 'No, we need bold strokes. We need this plan.', |
| 50 | /// style: TextStyle(fontWeight: FontWeight.bold), |
| 51 | /// ) |
| 52 | /// ``` |
| 53 | /// {@end-tool} |
| 54 | /// |
| 55 | /// ### Italics |
| 56 | /// |
| 57 | /// {@tool snippet} |
| 58 | /// As in the previous example, the [Text] widget is given a specific style |
| 59 | /// override which is implicitly mixed with the ambient [DefaultTextStyle]. |
| 60 | /// |
| 61 | ///  |
| 62 | /// |
| 63 | /// ```dart |
| 64 | /// const Text( |
| 65 | /// "Welcome to the present, we're running a real nation.", |
| 66 | /// style: TextStyle(fontStyle: FontStyle.italic), |
| 67 | /// ) |
| 68 | /// ``` |
| 69 | /// {@end-tool} |
| 70 | /// |
| 71 | /// ### Opacity and Color |
| 72 | /// |
| 73 | /// Each line here is progressively more opaque. The base color is |
| 74 | /// [Colors.black], and [Color.withOpacity] is used to create a |
| 75 | /// derivative color with the desired opacity. The root [TextSpan] for this |
| 76 | /// [RichText] widget is explicitly given the ambient [DefaultTextStyle], since |
| 77 | /// [RichText] does not do that automatically. The inner [TextStyle] objects are |
| 78 | /// implicitly mixed with the parent [TextSpan]'s [TextSpan.style]. |
| 79 | /// |
| 80 | /// If [color] is specified, [foreground] must be null and vice versa. [color] is |
| 81 | /// treated as a shorthand for `Paint()..color = color`. |
| 82 | /// |
| 83 | /// If [backgroundColor] is specified, [background] must be null and vice versa. |
| 84 | /// The [backgroundColor] is treated as a shorthand for |
| 85 | /// `background: Paint()..color = backgroundColor`. |
| 86 | /// |
| 87 | ///  |
| 88 | /// |
| 89 | /// ```dart |
| 90 | /// RichText( |
| 91 | /// text: TextSpan( |
| 92 | /// style: DefaultTextStyle.of(context).style, |
| 93 | /// children: <TextSpan>[ |
| 94 | /// TextSpan( |
| 95 | /// text: "You don't have the votes.\n", |
| 96 | /// style: TextStyle(color: Colors.black.withOpacity(0.6)), |
| 97 | /// ), |
| 98 | /// TextSpan( |
| 99 | /// text: "You don't have the votes!\n", |
| 100 | /// style: TextStyle(color: Colors.black.withOpacity(0.8)), |
| 101 | /// ), |
| 102 | /// TextSpan( |
| 103 | /// text: "You're gonna need congressional approval and you don't have the votes!\n", |
| 104 | /// style: TextStyle(color: Colors.black.withOpacity(1.0)), |
| 105 | /// ), |
| 106 | /// ], |
| 107 | /// ), |
| 108 | /// ) |
| 109 | /// ``` |
| 110 | /// |
| 111 | /// ### Size |
| 112 | /// |
| 113 | /// {@tool snippet} |
| 114 | /// In this example, the ambient [DefaultTextStyle] is explicitly manipulated to |
| 115 | /// obtain a [TextStyle] that doubles the default font size. |
| 116 | /// |
| 117 | ///  |
| 118 | /// |
| 119 | /// ```dart |
| 120 | /// Text( |
| 121 | /// "These are wise words, enterprising men quote 'em.", |
| 122 | /// style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 2.0), |
| 123 | /// ) |
| 124 | /// ``` |
| 125 | /// {@end-tool} |
| 126 | /// |
| 127 | /// ### Line height |
| 128 | /// |
| 129 | /// By default, text will layout with line height as defined by the font. |
| 130 | /// Font-metrics defined line height may be taller or shorter than the font size. |
| 131 | /// The [height] property allows manual adjustment of the height of the line as |
| 132 | /// a multiple of [fontSize]. For most fonts, setting [height] to 1.0 is not |
| 133 | /// the same as omitting or setting height to null. The following diagram |
| 134 | /// illustrates the difference between the font-metrics-defined line height and |
| 135 | /// the line height produced with `height: 1.0` (also known as the EM-square): |
| 136 | /// |
| 137 | ///  |
| 138 | /// |
| 139 | /// {@tool snippet} |
| 140 | /// The [height] property can be used to change the line height. Here, the line |
| 141 | /// height is set to 5 times the font size, so that the text is very spaced out. |
| 142 | /// Since the `fontSize` is set to 10, the final height of the line is |
| 143 | /// 50 pixels. |
| 144 | /// |
| 145 | /// ```dart |
| 146 | /// const Text( |
| 147 | /// 'Ladies and gentlemen, you coulda been anywhere in the world tonight, but you’re here with us in New York City.', |
| 148 | /// style: TextStyle(height: 5, fontSize: 10), |
| 149 | /// ) |
| 150 | /// ``` |
| 151 | /// {@end-tool} |
| 152 | /// |
| 153 | /// Examples of the resulting heights from different values of `TextStyle.height`: |
| 154 | /// |
| 155 | ///  |
| 156 | /// |
| 157 | /// See [StrutStyle] for further control of line height at the paragraph level. |
| 158 | /// |
| 159 | /// ### Leading Distribution and Trimming |
| 160 | /// |
| 161 | /// [Leading](https://en.wikipedia.org/wiki/Leading) is the vertical space |
| 162 | /// between glyphs from adjacent lines. Quantitatively, it is the line height |
| 163 | /// (see the previous section) subtracted by the font's ascent and descent. |
| 164 | /// It's possible to have a negative `Leading` if [height] is sufficiently |
| 165 | /// small. |
| 166 | /// |
| 167 | /// When the [height] multiplier is null, `leading` and how it is distributed |
| 168 | /// is up to the font's |
| 169 | /// [metrics](https://en.wikipedia.org/wiki/Typeface#Font_metrics). |
| 170 | /// When the [height] multiplier is specified, the exact behavior can be |
| 171 | /// configured via [leadingDistribution] and [TextPainter.textHeightBehavior]. |
| 172 | /// |
| 173 | ///  |
| 174 | /// |
| 175 | /// Above is a side-by-side comparison of different [leadingDistribution] and |
| 176 | /// [TextPainter.textHeightBehavior] combinations. |
| 177 | /// |
| 178 | /// * Configuration 1: The default. [leadingDistribution] is set to [TextLeadingDistribution.proportional]. |
| 179 | /// * Configuration 2: same as Configuration 1, except [TextHeightBehavior.applyHeightToFirstAscent] is set to false. |
| 180 | /// * Configuration 3: [leadingDistribution] is set to [TextLeadingDistribution.even]. |
| 181 | /// * Configuration 4: same as Configuration 3, except [TextHeightBehavior.applyHeightToLastDescent] is set to false. |
| 182 | /// |
| 183 | /// The [leadingDistribution] property controls how leading is distributed over |
| 184 | /// and under the text. With [TextLeadingDistribution.proportional] |
| 185 | /// (Configuration 1), `Top Leading : Bottom Leading = Font Ascent : Font |
| 186 | /// Descent`, which also means the alphabetic baseline divides the line height |
| 187 | /// into 2 parts proportional to the font's ascent and descent. With |
| 188 | /// [TextLeadingDistribution.even] (Configuration 3), `Top Leading` equals |
| 189 | /// `Bottom Leading`, and the glyphs are roughly centered within the allotted |
| 190 | /// line height. |
| 191 | /// |
| 192 | /// The [TextPainter.textHeightBehavior] is a property that controls leading at |
| 193 | /// the paragraph level. The `applyHeightToFirstAscent` property is applied |
| 194 | /// **after** [height] and [leadingDistribution]. Setting it to false trims the |
| 195 | /// "Top Leading" of the text box to match the font's ascent if it's on the |
| 196 | /// first line (see Configuration 2). Similarly setting |
| 197 | /// `applyHeightToLastDescent` to false reduces "Bottom Leading" to 0 for the |
| 198 | /// last line of text (Configuration 4). |
| 199 | /// |
| 200 | /// ### Wavy red underline with black text |
| 201 | /// |
| 202 | /// {@tool snippet} |
| 203 | /// Styles can be combined. In this example, the misspelled word is drawn in |
| 204 | /// black text and underlined with a wavy red line to indicate a spelling error. |
| 205 | /// (The remainder is styled according to the Flutter default text styles, not |
| 206 | /// the ambient [DefaultTextStyle], since no explicit style is given and |
| 207 | /// [RichText] does not automatically use the ambient [DefaultTextStyle].) |
| 208 | /// |
| 209 | ///  |
| 210 | /// |
| 211 | /// ```dart |
| 212 | /// RichText( |
| 213 | /// text: const TextSpan( |
| 214 | /// text: "Don't tax the South ", |
| 215 | /// children: <TextSpan>[ |
| 216 | /// TextSpan( |
| 217 | /// text: 'cuz', |
| 218 | /// style: TextStyle( |
| 219 | /// color: Colors.black, |
| 220 | /// decoration: TextDecoration.underline, |
| 221 | /// decorationColor: Colors.red, |
| 222 | /// decorationStyle: TextDecorationStyle.wavy, |
| 223 | /// ), |
| 224 | /// ), |
| 225 | /// TextSpan( |
| 226 | /// text: ' we got it made in the shade', |
| 227 | /// ), |
| 228 | /// ], |
| 229 | /// ), |
| 230 | /// ) |
| 231 | /// ``` |
| 232 | /// {@end-tool} |
| 233 | /// |
| 234 | /// ### Borders and stroke (Foreground) |
| 235 | /// |
| 236 | /// {@tool snippet} |
| 237 | /// To create bordered text, a [Paint] with [Paint.style] set to [PaintingStyle.stroke] |
| 238 | /// should be provided as a [foreground] paint. The following example uses a [Stack] |
| 239 | /// to produce a stroke and fill effect. |
| 240 | /// |
| 241 | ///  |
| 242 | /// |
| 243 | /// ```dart |
| 244 | /// Stack( |
| 245 | /// children: <Widget>[ |
| 246 | /// // Stroked text as border. |
| 247 | /// Text( |
| 248 | /// 'Greetings, planet!', |
| 249 | /// style: TextStyle( |
| 250 | /// fontSize: 40, |
| 251 | /// foreground: Paint() |
| 252 | /// ..style = PaintingStyle.stroke |
| 253 | /// ..strokeWidth = 6 |
| 254 | /// ..color = Colors.blue[700]!, |
| 255 | /// ), |
| 256 | /// ), |
| 257 | /// // Solid text as fill. |
| 258 | /// Text( |
| 259 | /// 'Greetings, planet!', |
| 260 | /// style: TextStyle( |
| 261 | /// fontSize: 40, |
| 262 | /// color: Colors.grey[300], |
| 263 | /// ), |
| 264 | /// ), |
| 265 | /// ], |
| 266 | /// ) |
| 267 | /// ``` |
| 268 | /// {@end-tool} |
| 269 | /// |
| 270 | /// ### Gradients (Foreground) |
| 271 | /// |
| 272 | /// {@tool snippet} |
| 273 | /// The [foreground] property also allows effects such as gradients to be |
| 274 | /// applied to the text. Here we provide a [Paint] with a [Gradient] |
| 275 | /// shader. |
| 276 | /// |
| 277 | ///  |
| 278 | /// |
| 279 | /// ```dart |
| 280 | /// Text( |
| 281 | /// 'Greetings, planet!', |
| 282 | /// style: TextStyle( |
| 283 | /// fontSize: 40, |
| 284 | /// foreground: Paint() |
| 285 | /// ..shader = ui.Gradient.linear( |
| 286 | /// const Offset(0, 20), |
| 287 | /// const Offset(150, 20), |
| 288 | /// <Color>[ |
| 289 | /// Colors.red, |
| 290 | /// Colors.yellow, |
| 291 | /// ], |
| 292 | /// ) |
| 293 | /// ), |
| 294 | /// ) |
| 295 | /// ``` |
| 296 | /// {@end-tool} |
| 297 | /// |
| 298 | /// ### Custom Fonts |
| 299 | /// |
| 300 | /// Custom fonts can be declared in the `pubspec.yaml` file as shown below: |
| 301 | /// |
| 302 | /// ```yaml |
| 303 | /// flutter: |
| 304 | /// fonts: |
| 305 | /// - family: Raleway |
| 306 | /// fonts: |
| 307 | /// - asset: fonts/Raleway-Regular.ttf |
| 308 | /// - asset: fonts/Raleway-Medium.ttf |
| 309 | /// weight: 500 |
| 310 | /// - asset: assets/fonts/Raleway-SemiBold.ttf |
| 311 | /// weight: 600 |
| 312 | /// - family: Schyler |
| 313 | /// fonts: |
| 314 | /// - asset: fonts/Schyler-Regular.ttf |
| 315 | /// - asset: fonts/Schyler-Italic.ttf |
| 316 | /// style: italic |
| 317 | /// ``` |
| 318 | /// |
| 319 | /// The `family` property determines the name of the font, which you can use in |
| 320 | /// the [fontFamily] argument. The `asset` property is a path to the font file, |
| 321 | /// relative to the `pubspec.yaml` file. The `weight` property specifies the |
| 322 | /// weight of the glyph outlines in the file as an integer multiple of 100 |
| 323 | /// between 100 and 900. This corresponds to the [FontWeight] class and can be |
| 324 | /// used in the [fontWeight] argument. The `style` property specifies whether the |
| 325 | /// outlines in the file are `italic` or `normal`. These values correspond to |
| 326 | /// the [FontStyle] class and can be used in the [fontStyle] argument. |
| 327 | /// |
| 328 | /// To select a custom font, create [TextStyle] using the [fontFamily] |
| 329 | /// argument as shown in the example below: |
| 330 | /// |
| 331 | /// {@tool snippet} |
| 332 | ///  |
| 333 | /// |
| 334 | /// ```dart |
| 335 | /// const TextStyle(fontFamily: 'Raleway') |
| 336 | /// ``` |
| 337 | /// {@end-tool} |
| 338 | /// |
| 339 | /// To use a font family defined in a package, the `package` argument must be |
| 340 | /// provided. For instance, suppose the font declaration above is in the |
| 341 | /// `pubspec.yaml` of a package named `my_package` which the app depends on. |
| 342 | /// Then creating the TextStyle is done as follows: |
| 343 | /// |
| 344 | /// ```dart |
| 345 | /// const TextStyle(fontFamily: 'Raleway', package: 'my_package') |
| 346 | /// ``` |
| 347 | /// |
| 348 | /// If the package internally uses the font it defines, it should still specify |
| 349 | /// the `package` argument when creating the text style as in the example above. |
| 350 | /// |
| 351 | /// A package can also provide font files without declaring a font in its |
| 352 | /// `pubspec.yaml`. These files should then be in the `lib/` folder of the |
| 353 | /// package. The font files will not automatically be bundled in the app, instead |
| 354 | /// the app can use these selectively when declaring a font. Suppose a package |
| 355 | /// named `my_package` has: |
| 356 | /// |
| 357 | /// lib/fonts/Raleway-Medium.ttf |
| 358 | /// |
| 359 | /// Then the app can declare a font like in the example below: |
| 360 | /// |
| 361 | /// ```yaml |
| 362 | /// flutter: |
| 363 | /// fonts: |
| 364 | /// - family: Raleway |
| 365 | /// fonts: |
| 366 | /// - asset: assets/fonts/Raleway-Regular.ttf |
| 367 | /// - asset: packages/my_package/fonts/Raleway-Medium.ttf |
| 368 | /// weight: 500 |
| 369 | /// ``` |
| 370 | /// |
| 371 | /// The `lib/` is implied, so it should not be included in the asset path. |
| 372 | /// |
| 373 | /// In this case, since the app locally defines the font, the TextStyle is |
| 374 | /// created without the `package` argument: |
| 375 | /// |
| 376 | /// {@tool snippet} |
| 377 | /// ```dart |
| 378 | /// const TextStyle(fontFamily: 'Raleway') |
| 379 | /// ``` |
| 380 | /// {@end-tool} |
| 381 | /// |
| 382 | /// #### Supported font formats |
| 383 | /// |
| 384 | /// Font formats currently supported by Flutter: |
| 385 | /// |
| 386 | /// * `.ttc` |
| 387 | /// * `.ttf` |
| 388 | /// * `.otf` |
| 389 | /// |
| 390 | /// Flutter does not support `.woff` and `.woff2` fonts for all platforms. |
| 391 | /// |
| 392 | /// ### Custom Font Fallback |
| 393 | /// |
| 394 | /// A custom [fontFamilyFallback] list can be provided. The list should be an |
| 395 | /// ordered list of strings of font family names in the order they will be attempted. |
| 396 | /// |
| 397 | /// The fonts in [fontFamilyFallback] will be used only if the requested glyph is |
| 398 | /// not present in the [fontFamily]. |
| 399 | /// |
| 400 | /// The fallback order is: |
| 401 | /// |
| 402 | /// * [fontFamily] |
| 403 | /// * [fontFamilyFallback] in order of first to last. |
| 404 | /// * System fallback fonts which will vary depending on platform. |
| 405 | /// |
| 406 | /// The glyph used will always be the first matching version in fallback order. |
| 407 | /// |
| 408 | /// The [fontFamilyFallback] property is commonly used to specify different font |
| 409 | /// families for multilingual text spans as well as separate fonts for glyphs such |
| 410 | /// as emojis. |
| 411 | /// |
| 412 | /// {@tool snippet} |
| 413 | /// In the following example, any glyphs not present in the font `Raleway` will be attempted |
| 414 | /// to be resolved with `Noto Sans CJK SC`, and then with `Noto Color Emoji`: |
| 415 | /// |
| 416 | /// ```dart |
| 417 | /// const TextStyle( |
| 418 | /// fontFamily: 'Raleway', |
| 419 | /// fontFamilyFallback: <String>[ |
| 420 | /// 'Noto Sans CJK SC', |
| 421 | /// 'Noto Color Emoji', |
| 422 | /// ], |
| 423 | /// ) |
| 424 | /// ``` |
| 425 | /// {@end-tool} |
| 426 | /// |
| 427 | /// If all custom fallback font families are exhausted and no match was found |
| 428 | /// or no custom fallback was provided, the platform font fallback will be used. |
| 429 | /// |
| 430 | /// ### Inconsistent platform fonts |
| 431 | /// |
| 432 | /// By default, fonts differ depending on the platform. |
| 433 | /// |
| 434 | /// * The default font-family for `Android`,`Fuchsia` and `Linux` is `Roboto`. |
| 435 | /// * The default font-family for `iOS` is `SF Pro Display`/`SF Pro Text`. |
| 436 | /// * The default font-family for `MacOS` is `.AppleSystemUIFont`. |
| 437 | /// * The default font-family for `Windows` is `Segoe UI`. |
| 438 | // |
| 439 | // The implementation of these defaults can be found in: |
| 440 | // /packages/flutter/lib/src/material/typography.dart |
| 441 | /// |
| 442 | /// Since Flutter's font discovery for default fonts depends on the fonts present |
| 443 | /// on the device, it is not safe to assume all default fonts will be available or |
| 444 | /// consistent across devices. |
| 445 | /// |
| 446 | /// A known example of this is that Samsung devices ship with a CJK font that has |
| 447 | /// smaller line spacing than the Android default. This results in Samsung devices |
| 448 | /// displaying more tightly spaced text than on other Android devices when no |
| 449 | /// custom font is specified. |
| 450 | /// |
| 451 | /// To avoid this, a custom font should be specified if absolute font consistency |
| 452 | /// is required for your application. |
| 453 | /// |
| 454 | /// See also: |
| 455 | /// |
| 456 | /// * [Text], the widget for showing text in a single style. |
| 457 | /// * [DefaultTextStyle], the widget that specifies the default text styles for |
| 458 | /// [Text] widgets, configured using a [TextStyle]. |
| 459 | /// * [RichText], the widget for showing a paragraph of mix-style text. |
| 460 | /// * [TextSpan], the class that wraps a [TextStyle] for the purposes of |
| 461 | /// passing it to a [RichText]. |
| 462 | /// * [TextStyle](https://api.flutter.dev/flutter/dart-ui/TextStyle-class.html), the class in the [dart:ui] library. |
| 463 | /// * Cookbook: [Use a custom font](https://docs.flutter.dev/cookbook/design/fonts) |
| 464 | /// * Cookbook: [Use themes to share colors and font styles](https://docs.flutter.dev/cookbook/design/themes) |
| 465 | @immutable |
| 466 | class TextStyle with Diagnosticable { |
| 467 | /// Creates a text style. |
| 468 | /// |
| 469 | /// The `package` argument must be non-null if the font family is defined in a |
| 470 | /// package. It is combined with the `fontFamily` argument to set the |
| 471 | /// [fontFamily] property. |
| 472 | /// |
| 473 | /// On Apple devices the strings 'CupertinoSystemText' and |
| 474 | /// 'CupertinoSystemDisplay' are used in [fontFamily] as proxies for the |
| 475 | /// Apple system fonts. They currently redirect to the equivalent of SF Pro |
| 476 | /// Text and SF Pro Display respectively. 'CupertinoSystemText' is designed |
| 477 | /// for fonts below 20 point size, and 'CupertinoSystemDisplay' is recommended |
| 478 | /// for sizes 20 and above. When used on non-Apple platforms, these strings |
| 479 | /// will return the regular fallback font family instead. |
| 480 | const TextStyle({ |
| 481 | this.inherit = true, |
| 482 | this.color, |
| 483 | this.backgroundColor, |
| 484 | this.fontSize, |
| 485 | this.fontWeight, |
| 486 | this.fontStyle, |
| 487 | this.letterSpacing, |
| 488 | this.wordSpacing, |
| 489 | this.textBaseline, |
| 490 | this.height, |
| 491 | this.leadingDistribution, |
| 492 | this.locale, |
| 493 | this.foreground, |
| 494 | this.background, |
| 495 | this.shadows, |
| 496 | this.fontFeatures, |
| 497 | this.fontVariations, |
| 498 | this.decoration, |
| 499 | this.decorationColor, |
| 500 | this.decorationStyle, |
| 501 | this.decorationThickness, |
| 502 | this.debugLabel, |
| 503 | String? fontFamily, |
| 504 | List<String>? fontFamilyFallback, |
| 505 | String? package, |
| 506 | this.overflow, |
| 507 | }) : fontFamily = package == null ? fontFamily : 'packages/ $package/ $fontFamily' , |
| 508 | _fontFamilyFallback = fontFamilyFallback, |
| 509 | _package = package, |
| 510 | assert(color == null || foreground == null, _kColorForegroundWarning), |
| 511 | assert(backgroundColor == null || background == null, _kColorBackgroundWarning); |
| 512 | |
| 513 | /// Whether null values in this [TextStyle] can be replaced with their value |
| 514 | /// in another [TextStyle] using [merge]. |
| 515 | /// |
| 516 | /// The [merge] operation is not commutative: the [inherit] value of the |
| 517 | /// method argument decides whether the two [TextStyle]s can be combined |
| 518 | /// together. If it is false, the method argument [TextStyle] will be returned. |
| 519 | /// Otherwise, the combining is allowed, and the returned [TextStyle] inherits |
| 520 | /// the [inherit] value from the method receiver. |
| 521 | /// |
| 522 | /// This property does not affect the text style inheritance in an [InlineSpan] |
| 523 | /// tree: an [InlineSpan]'s text style is merged with that of an ancestor |
| 524 | /// [InlineSpan] if it has unspecified fields, regardless of its [inherit] |
| 525 | /// value. |
| 526 | /// |
| 527 | /// Properties that don't have explicit values or other default values to fall |
| 528 | /// back to will revert to the defaults: white in color, a font size of 14 |
| 529 | /// pixels, in a sans-serif font face. |
| 530 | /// |
| 531 | /// See also: |
| 532 | /// * [TextStyle.merge], which can be used to combine properties from two |
| 533 | /// [TextStyle]s. |
| 534 | final bool inherit; |
| 535 | |
| 536 | /// The color to use when painting the text. |
| 537 | /// |
| 538 | /// If [foreground] is specified, this value must be null. The [color] property |
| 539 | /// is shorthand for `Paint()..color = color`. |
| 540 | /// |
| 541 | /// In [merge], [apply], and [lerp], conflicts between [color] and [foreground] |
| 542 | /// specification are resolved in [foreground]'s favor - i.e. if [foreground] is |
| 543 | /// specified in one place, it will dominate [color] in another. |
| 544 | final Color? color; |
| 545 | |
| 546 | /// The color to use as the background for the text. |
| 547 | /// |
| 548 | /// If [background] is specified, this value must be null. The |
| 549 | /// [backgroundColor] property is shorthand for |
| 550 | /// `background: Paint()..color = backgroundColor`. |
| 551 | /// |
| 552 | /// In [merge], [apply], and [lerp], conflicts between [backgroundColor] and [background] |
| 553 | /// specification are resolved in [background]'s favor - i.e. if [background] is |
| 554 | /// specified in one place, it will dominate [color] in another. |
| 555 | final Color? backgroundColor; |
| 556 | |
| 557 | /// The name of the font to use when painting the text (e.g., Roboto). |
| 558 | /// |
| 559 | /// If the font is defined in a package, this will be prefixed with |
| 560 | /// 'packages/package_name/' (e.g. 'packages/cool_fonts/Roboto'). The |
| 561 | /// prefixing is done by the constructor when the `package` argument is |
| 562 | /// provided. |
| 563 | /// |
| 564 | /// The value provided in [fontFamily] will act as the preferred/first font |
| 565 | /// family that glyphs are looked for in, followed in order by the font families |
| 566 | /// in [fontFamilyFallback]. When [fontFamily] is null or not provided, the |
| 567 | /// first value in [fontFamilyFallback] acts as the preferred/first font |
| 568 | /// family. When neither is provided, then the default platform font will |
| 569 | /// be used. |
| 570 | /// |
| 571 | /// When running on Apple devices, the strings 'CupertinoSystemText' and |
| 572 | /// 'CupertinoSystemDisplay' are used as proxies for the Apple system fonts. |
| 573 | /// They currently redirect to the equivalent of SF Pro Text and SF Pro Display |
| 574 | /// respectively. 'CupertinoSystemText' is designed for fonts below 20 point |
| 575 | /// size, and 'CupertinoSystemDisplay' is recommended for sizes 20 and above. |
| 576 | /// When used on non-Apple platforms, these strings will return the regular |
| 577 | /// fallback font family instead. |
| 578 | final String? fontFamily; |
| 579 | |
| 580 | /// The ordered list of font families to fall back on when a glyph cannot be |
| 581 | /// found in a higher priority font family. |
| 582 | /// |
| 583 | /// The value provided in [fontFamily] will act as the preferred/first font |
| 584 | /// family that glyphs are looked for in, followed in order by the font families |
| 585 | /// in [fontFamilyFallback]. If all font families are exhausted and no match |
| 586 | /// was found, the default platform font family will be used instead. |
| 587 | /// |
| 588 | /// When [fontFamily] is null or not provided, the first value in [fontFamilyFallback] |
| 589 | /// acts as the preferred/first font family. When neither is provided, then |
| 590 | /// the default platform font will be used. Providing an empty list or null |
| 591 | /// for this property is the same as omitting it. |
| 592 | /// |
| 593 | /// For example, if a glyph is not found in [fontFamily], then each font family |
| 594 | /// in [fontFamilyFallback] will be searched in order until it is found. If it |
| 595 | /// is not found, then a box will be drawn in its place. |
| 596 | /// |
| 597 | /// If the font is defined in a package, each font family in the list will be |
| 598 | /// prefixed with 'packages/package_name/' (e.g. 'packages/cool_fonts/Roboto'). |
| 599 | /// The package name should be provided by the `package` argument in the |
| 600 | /// constructor. |
| 601 | List<String>? get fontFamilyFallback => _package == null |
| 602 | ? _fontFamilyFallback |
| 603 | : _fontFamilyFallback?.map((String str) => 'packages/ $_package/ $str' ).toList(); |
| 604 | final List<String>? _fontFamilyFallback; |
| 605 | |
| 606 | // This is stored in order to prefix the fontFamilies in _fontFamilyFallback |
| 607 | // in the [fontFamilyFallback] getter. |
| 608 | final String? _package; |
| 609 | |
| 610 | /// The size of fonts (in logical pixels) to use when painting the text. |
| 611 | /// |
| 612 | /// The value specified matches the dimension of the |
| 613 | /// [em square](https://fonts.google.com/knowledge/glossary/em) of the |
| 614 | /// underlying font, and more often then not isn't exactly the height or the |
| 615 | /// width of glyphs in the font. |
| 616 | /// |
| 617 | /// During painting, the [fontSize] is multiplied by the current |
| 618 | /// `textScaleFactor` to let users make it easier to read text by increasing |
| 619 | /// its size. |
| 620 | /// |
| 621 | /// The [getParagraphStyle] method defaults to 14 logical pixels if [fontSize] |
| 622 | /// is set to null. |
| 623 | final double? fontSize; |
| 624 | |
| 625 | /// The typeface thickness to use when painting the text (e.g., bold). |
| 626 | final FontWeight? fontWeight; |
| 627 | |
| 628 | /// The typeface variant to use when drawing the letters (e.g., italics). |
| 629 | final FontStyle? fontStyle; |
| 630 | |
| 631 | /// The amount of space (in logical pixels) to add between each letter. |
| 632 | /// A negative value can be used to bring the letters closer. |
| 633 | final double? letterSpacing; |
| 634 | |
| 635 | /// The amount of space (in logical pixels) to add at each sequence of |
| 636 | /// white-space (i.e. between each word). A negative value can be used to |
| 637 | /// bring the words closer. |
| 638 | final double? wordSpacing; |
| 639 | |
| 640 | /// The common baseline that should be aligned between this text span and its |
| 641 | /// parent text span, or, for the root text spans, with the line box. |
| 642 | final TextBaseline? textBaseline; |
| 643 | |
| 644 | /// The height of this text span, as a multiple of the font size. |
| 645 | /// |
| 646 | /// When [height] is [kTextHeightNone], the line height will be determined by |
| 647 | /// the font's metrics directly, which may differ from the fontSize. Otherwise |
| 648 | /// the line height of the span of text will be a multiple of [fontSize], |
| 649 | /// and be exactly `fontSize * height` logical pixels tall. |
| 650 | /// |
| 651 | /// For most fonts, setting [height] to 1.0 is not the same as setting height |
| 652 | /// to [kTextHeightNone] because the [fontSize] sets the height of the EM-square, |
| 653 | /// which is different than the font provided metrics for line height. The |
| 654 | /// following diagram illustrates the difference between the font-metrics |
| 655 | /// defined line height and the line height produced with `height: 1.0` |
| 656 | /// (which forms the upper and lower edges of the EM-square): |
| 657 | /// |
| 658 | ///  |
| 659 | /// |
| 660 | /// Examples of the resulting line heights from different values of `TextStyle.height`: |
| 661 | /// |
| 662 | ///  |
| 663 | /// |
| 664 | /// See [StrutStyle] and [TextHeightBehavior] for further control of line |
| 665 | /// height at the paragraph level. |
| 666 | final double? height; |
| 667 | |
| 668 | /// How the vertical space added by the [height] multiplier should be |
| 669 | /// distributed over and under the text. |
| 670 | /// |
| 671 | /// When a non-null [height] is specified, after accommodating the glyphs of |
| 672 | /// the text, the remaining vertical space from the allotted line height will |
| 673 | /// be distributed over and under the text, according to the |
| 674 | /// [leadingDistribution] property. See the [TextStyle] class's documentation |
| 675 | /// for an example. |
| 676 | /// |
| 677 | /// When [height] is null, [leadingDistribution] does not affect the text |
| 678 | /// layout. |
| 679 | /// |
| 680 | /// Defaults to null, which defers to the paragraph's |
| 681 | /// `ParagraphStyle.textHeightBehavior`'s [leadingDistribution]. |
| 682 | final TextLeadingDistribution? leadingDistribution; |
| 683 | |
| 684 | /// The locale used to select region-specific glyphs. |
| 685 | /// |
| 686 | /// This property is rarely set. Typically the locale used to select |
| 687 | /// region-specific glyphs is defined by the text widget's [BuildContext] |
| 688 | /// using `Localizations.localeOf(context)`. For example [RichText] defines |
| 689 | /// its locale this way. However, a rich text widget's [TextSpan]s could |
| 690 | /// specify text styles with different explicit locales in order to select |
| 691 | /// different region-specific glyphs for each text span. |
| 692 | final Locale? locale; |
| 693 | |
| 694 | /// The paint drawn as a foreground for the text. |
| 695 | /// |
| 696 | /// The value should ideally be cached and reused each time if multiple text |
| 697 | /// styles are created with the same paint settings. Otherwise, each time it |
| 698 | /// will appear like the style changed, which will result in unnecessary |
| 699 | /// updates all the way through the framework. |
| 700 | /// |
| 701 | /// If [color] is specified, this value must be null. The [color] property |
| 702 | /// is shorthand for `Paint()..color = color`. |
| 703 | /// |
| 704 | /// In [merge], [apply], and [lerp], conflicts between [color] and [foreground] |
| 705 | /// specification are resolved in [foreground]'s favor - i.e. if [foreground] is |
| 706 | /// specified in one place, it will dominate [color] in another. |
| 707 | final Paint? foreground; |
| 708 | |
| 709 | /// The paint drawn as a background for the text. |
| 710 | /// |
| 711 | /// The value should ideally be cached and reused each time if multiple text |
| 712 | /// styles are created with the same paint settings. Otherwise, each time it |
| 713 | /// will appear like the style changed, which will result in unnecessary |
| 714 | /// updates all the way through the framework. |
| 715 | /// |
| 716 | /// If [backgroundColor] is specified, this value must be null. The |
| 717 | /// [backgroundColor] property is shorthand for |
| 718 | /// `background: Paint()..color = backgroundColor`. |
| 719 | /// |
| 720 | /// In [merge], [apply], and [lerp], conflicts between [backgroundColor] and |
| 721 | /// [background] specification are resolved in [background]'s favor - i.e. if |
| 722 | /// [background] is specified in one place, it will dominate [backgroundColor] |
| 723 | /// in another. |
| 724 | final Paint? background; |
| 725 | |
| 726 | /// The decorations to paint near the text (e.g., an underline). |
| 727 | /// |
| 728 | /// Multiple decorations can be applied using [TextDecoration.combine]. |
| 729 | final TextDecoration? decoration; |
| 730 | |
| 731 | /// The color in which to paint the text decorations. |
| 732 | final Color? decorationColor; |
| 733 | |
| 734 | /// The style in which to paint the text decorations (e.g., dashed). |
| 735 | final TextDecorationStyle? decorationStyle; |
| 736 | |
| 737 | /// The thickness of the decoration stroke as a multiplier of the thickness |
| 738 | /// defined by the font. |
| 739 | /// |
| 740 | /// The font provides a base stroke width for [decoration]s which scales off |
| 741 | /// of the [fontSize]. This property may be used to achieve a thinner or |
| 742 | /// thicker decoration stroke, without changing the [fontSize]. For example, |
| 743 | /// a [decorationThickness] of 2.0 will draw a decoration twice as thick as |
| 744 | /// the font defined decoration thickness. |
| 745 | /// |
| 746 | /// {@tool snippet} |
| 747 | /// To achieve a bolded strike-through, we can apply a thicker stroke for the |
| 748 | /// decoration. |
| 749 | /// |
| 750 | /// ```dart |
| 751 | /// const Text( |
| 752 | /// 'This has a very BOLD strike through!', |
| 753 | /// style: TextStyle( |
| 754 | /// decoration: TextDecoration.lineThrough, |
| 755 | /// decorationThickness: 2.85, |
| 756 | /// ), |
| 757 | /// ) |
| 758 | /// ``` |
| 759 | /// {@end-tool} |
| 760 | /// |
| 761 | /// {@tool snippet} |
| 762 | /// We can apply a very thin and subtle wavy underline (perhaps, when words |
| 763 | /// are misspelled) by using a [decorationThickness] < 1.0. |
| 764 | /// |
| 765 | /// ```dart |
| 766 | /// const Text( |
| 767 | /// 'oopsIforgottousespaces!', |
| 768 | /// style: TextStyle( |
| 769 | /// decoration: TextDecoration.underline, |
| 770 | /// decorationStyle: TextDecorationStyle.wavy, |
| 771 | /// decorationColor: Colors.red, |
| 772 | /// decorationThickness: 0.5, |
| 773 | /// ), |
| 774 | /// ) |
| 775 | /// ``` |
| 776 | /// {@end-tool} |
| 777 | /// |
| 778 | /// The default [decorationThickness] is 1.0, which will use the font's base |
| 779 | /// stroke thickness/width. |
| 780 | final double? decorationThickness; |
| 781 | |
| 782 | /// A human-readable description of this text style. |
| 783 | /// |
| 784 | /// This property is maintained only in debug builds. |
| 785 | /// |
| 786 | /// When merging ([merge]), copying ([copyWith]), modifying using [apply], or |
| 787 | /// interpolating ([lerp]), the label of the resulting style is marked with |
| 788 | /// the debug labels of the original styles. This helps figuring out where a |
| 789 | /// particular text style came from. |
| 790 | /// |
| 791 | /// This property is not considered when comparing text styles using `==` or |
| 792 | /// [compareTo], and it does not affect [hashCode]. |
| 793 | final String? debugLabel; |
| 794 | |
| 795 | /// A list of [Shadow]s that will be painted underneath the text. |
| 796 | /// |
| 797 | /// Multiple shadows are supported to replicate lighting from multiple light |
| 798 | /// sources. |
| 799 | /// |
| 800 | /// Shadows must be in the same order for [TextStyle] to be considered as |
| 801 | /// equivalent as order produces differing transparency. |
| 802 | final List<Shadow>? shadows; |
| 803 | |
| 804 | /// A list of [FontFeature]s that affect how the font selects glyphs. |
| 805 | /// |
| 806 | /// Some fonts support multiple variants of how a given character can be |
| 807 | /// rendered. For example, a font might provide both proportional and |
| 808 | /// tabular numbers, or it might offer versions of the zero digit with |
| 809 | /// and without slashes. [FontFeature]s can be used to select which of |
| 810 | /// these variants will be used for rendering. |
| 811 | /// |
| 812 | /// Font features are not interpolated by [lerp]. |
| 813 | /// |
| 814 | /// See also: |
| 815 | /// |
| 816 | /// * [fontVariations], for font features that have continuous parameters. |
| 817 | final List<FontFeature>? fontFeatures; |
| 818 | |
| 819 | /// A list of [FontVariation]s that affect how a variable font is rendered. |
| 820 | /// |
| 821 | /// Some fonts are variable fonts that can generate multiple font faces based |
| 822 | /// on the values of customizable attributes. For example, a variable font |
| 823 | /// may have a weight axis that can be set to a value between 1 and 1000. |
| 824 | /// [FontVariation]s can be used to select the values of these design axes. |
| 825 | /// |
| 826 | /// For example, to control the weight axis of the Roboto Slab variable font |
| 827 | /// (https://fonts.google.com/specimen/Roboto+Slab): |
| 828 | /// ```dart |
| 829 | /// const TextStyle( |
| 830 | /// fontFamily: 'RobotoSlab', |
| 831 | /// fontVariations: <FontVariation>[FontVariation('wght', 900.0)] |
| 832 | /// ) |
| 833 | /// ``` |
| 834 | /// |
| 835 | /// Font variations can be interpolated via [lerp]. This is fastest when the |
| 836 | /// same font variation axes are specified, in the same order, in both |
| 837 | /// [TextStyle] objects. See [lerpFontVariations]. |
| 838 | /// |
| 839 | /// See also: |
| 840 | /// |
| 841 | /// * [fontFeatures], for font variations that have discrete values. |
| 842 | final List<FontVariation>? fontVariations; |
| 843 | |
| 844 | /// How visual text overflow should be handled. |
| 845 | final TextOverflow? overflow; |
| 846 | |
| 847 | // Return the original value of fontFamily, without the additional |
| 848 | // "packages/$_package/" prefix. |
| 849 | String? get _fontFamily { |
| 850 | if (_package != null) { |
| 851 | final String fontFamilyPrefix = 'packages/ $_package/' ; |
| 852 | assert(fontFamily?.startsWith(fontFamilyPrefix) ?? true); |
| 853 | return fontFamily?.substring(fontFamilyPrefix.length); |
| 854 | } |
| 855 | return fontFamily; |
| 856 | } |
| 857 | |
| 858 | /// Creates a copy of this text style but with the given fields replaced with |
| 859 | /// the new values. |
| 860 | /// |
| 861 | /// One of [color] or [foreground] must be null, and if this has [foreground] |
| 862 | /// specified it will be given preference over any color parameter. |
| 863 | /// |
| 864 | /// One of [backgroundColor] or [background] must be null, and if this has |
| 865 | /// [background] specified it will be given preference over any |
| 866 | /// backgroundColor parameter. |
| 867 | TextStyle copyWith({ |
| 868 | bool? inherit, |
| 869 | Color? color, |
| 870 | Color? backgroundColor, |
| 871 | double? fontSize, |
| 872 | FontWeight? fontWeight, |
| 873 | FontStyle? fontStyle, |
| 874 | double? letterSpacing, |
| 875 | double? wordSpacing, |
| 876 | TextBaseline? textBaseline, |
| 877 | double? height, |
| 878 | TextLeadingDistribution? leadingDistribution, |
| 879 | Locale? locale, |
| 880 | Paint? foreground, |
| 881 | Paint? background, |
| 882 | List<Shadow>? shadows, |
| 883 | List<FontFeature>? fontFeatures, |
| 884 | List<FontVariation>? fontVariations, |
| 885 | TextDecoration? decoration, |
| 886 | Color? decorationColor, |
| 887 | TextDecorationStyle? decorationStyle, |
| 888 | double? decorationThickness, |
| 889 | String? debugLabel, |
| 890 | String? fontFamily, |
| 891 | List<String>? fontFamilyFallback, |
| 892 | String? package, |
| 893 | TextOverflow? overflow, |
| 894 | }) { |
| 895 | assert(color == null || foreground == null, _kColorForegroundWarning); |
| 896 | assert(backgroundColor == null || background == null, _kColorBackgroundWarning); |
| 897 | String? newDebugLabel; |
| 898 | assert(() { |
| 899 | if (debugLabel != null) { |
| 900 | newDebugLabel = debugLabel; |
| 901 | } else if (this.debugLabel != null) { |
| 902 | newDebugLabel = '( ${this.debugLabel}).copyWith' ; |
| 903 | } |
| 904 | return true; |
| 905 | }()); |
| 906 | |
| 907 | return TextStyle( |
| 908 | inherit: inherit ?? this.inherit, |
| 909 | color: this.foreground == null && foreground == null ? color ?? this.color : null, |
| 910 | backgroundColor: this.background == null && background == null |
| 911 | ? backgroundColor ?? this.backgroundColor |
| 912 | : null, |
| 913 | fontSize: fontSize ?? this.fontSize, |
| 914 | fontWeight: fontWeight ?? this.fontWeight, |
| 915 | fontStyle: fontStyle ?? this.fontStyle, |
| 916 | letterSpacing: letterSpacing ?? this.letterSpacing, |
| 917 | wordSpacing: wordSpacing ?? this.wordSpacing, |
| 918 | textBaseline: textBaseline ?? this.textBaseline, |
| 919 | height: height ?? this.height, |
| 920 | leadingDistribution: leadingDistribution ?? this.leadingDistribution, |
| 921 | locale: locale ?? this.locale, |
| 922 | foreground: foreground ?? this.foreground, |
| 923 | background: background ?? this.background, |
| 924 | shadows: shadows ?? this.shadows, |
| 925 | fontFeatures: fontFeatures ?? this.fontFeatures, |
| 926 | fontVariations: fontVariations ?? this.fontVariations, |
| 927 | decoration: decoration ?? this.decoration, |
| 928 | decorationColor: decorationColor ?? this.decorationColor, |
| 929 | decorationStyle: decorationStyle ?? this.decorationStyle, |
| 930 | decorationThickness: decorationThickness ?? this.decorationThickness, |
| 931 | debugLabel: newDebugLabel, |
| 932 | fontFamily: fontFamily ?? _fontFamily, |
| 933 | fontFamilyFallback: fontFamilyFallback ?? _fontFamilyFallback, |
| 934 | package: package ?? _package, |
| 935 | overflow: overflow ?? this.overflow, |
| 936 | ); |
| 937 | } |
| 938 | |
| 939 | /// Creates a copy of this text style replacing or altering the specified |
| 940 | /// properties. |
| 941 | /// |
| 942 | /// The non-numeric properties [color], [fontFamily], [decoration], |
| 943 | /// [decorationColor] and [decorationStyle] are replaced with the new values. |
| 944 | /// |
| 945 | /// [foreground] will be given preference over [color] if it is not null and |
| 946 | /// [background] will be given preference over [backgroundColor] if it is not |
| 947 | /// null. |
| 948 | /// |
| 949 | /// The numeric properties are multiplied by the given factors and then |
| 950 | /// incremented by the given deltas. |
| 951 | /// |
| 952 | /// For example, `style.apply(fontSizeFactor: 2.0, fontSizeDelta: 1.0)` would |
| 953 | /// return a [TextStyle] whose [fontSize] is `style.fontSize * 2.0 + 1.0`. |
| 954 | /// |
| 955 | /// For the [fontWeight], the delta is applied to the [FontWeight] enum index |
| 956 | /// values, so that for instance `style.apply(fontWeightDelta: -2)` when |
| 957 | /// applied to a `style` whose [fontWeight] is [FontWeight.w500] will return a |
| 958 | /// [TextStyle] with a [FontWeight.w300]. |
| 959 | /// |
| 960 | /// If the underlying values are null, then the corresponding factors and/or |
| 961 | /// deltas must not be specified. Additionally, if [height] is [kTextHeightNone] |
| 962 | /// it will not be modified by this method. |
| 963 | /// |
| 964 | /// If [foreground] is specified on this object, then applying [color] here |
| 965 | /// will have no effect and if [background] is specified on this object, then |
| 966 | /// applying [backgroundColor] here will have no effect either. |
| 967 | TextStyle apply({ |
| 968 | Color? color, |
| 969 | Color? backgroundColor, |
| 970 | TextDecoration? decoration, |
| 971 | Color? decorationColor, |
| 972 | TextDecorationStyle? decorationStyle, |
| 973 | double decorationThicknessFactor = 1.0, |
| 974 | double decorationThicknessDelta = 0.0, |
| 975 | String? fontFamily, |
| 976 | List<String>? fontFamilyFallback, |
| 977 | double fontSizeFactor = 1.0, |
| 978 | double fontSizeDelta = 0.0, |
| 979 | int fontWeightDelta = 0, |
| 980 | FontStyle? fontStyle, |
| 981 | double letterSpacingFactor = 1.0, |
| 982 | double letterSpacingDelta = 0.0, |
| 983 | double wordSpacingFactor = 1.0, |
| 984 | double wordSpacingDelta = 0.0, |
| 985 | double heightFactor = 1.0, |
| 986 | double heightDelta = 0.0, |
| 987 | TextBaseline? textBaseline, |
| 988 | TextLeadingDistribution? leadingDistribution, |
| 989 | Locale? locale, |
| 990 | List<Shadow>? shadows, |
| 991 | List<FontFeature>? fontFeatures, |
| 992 | List<FontVariation>? fontVariations, |
| 993 | String? package, |
| 994 | TextOverflow? overflow, |
| 995 | }) { |
| 996 | assert(fontSize != null || (fontSizeFactor == 1.0 && fontSizeDelta == 0.0)); |
| 997 | assert(fontWeight != null || fontWeightDelta == 0.0); |
| 998 | assert(letterSpacing != null || (letterSpacingFactor == 1.0 && letterSpacingDelta == 0.0)); |
| 999 | assert(wordSpacing != null || (wordSpacingFactor == 1.0 && wordSpacingDelta == 0.0)); |
| 1000 | assert( |
| 1001 | decorationThickness != null || |
| 1002 | (decorationThicknessFactor == 1.0 && decorationThicknessDelta == 0.0), |
| 1003 | ); |
| 1004 | |
| 1005 | String? modifiedDebugLabel; |
| 1006 | assert(() { |
| 1007 | if (debugLabel != null) { |
| 1008 | modifiedDebugLabel = '( $debugLabel).apply' ; |
| 1009 | } |
| 1010 | return true; |
| 1011 | }()); |
| 1012 | |
| 1013 | return TextStyle( |
| 1014 | inherit: inherit, |
| 1015 | color: foreground == null ? color ?? this.color : null, |
| 1016 | backgroundColor: background == null ? backgroundColor ?? this.backgroundColor : null, |
| 1017 | fontFamily: fontFamily ?? _fontFamily, |
| 1018 | fontFamilyFallback: fontFamilyFallback ?? _fontFamilyFallback, |
| 1019 | fontSize: fontSize == null ? null : fontSize! * fontSizeFactor + fontSizeDelta, |
| 1020 | fontWeight: fontWeight == null |
| 1021 | ? null |
| 1022 | : FontWeight.values[(fontWeight!.index + fontWeightDelta).clamp( |
| 1023 | 0, |
| 1024 | FontWeight.values.length - 1, |
| 1025 | )], |
| 1026 | fontStyle: fontStyle ?? this.fontStyle, |
| 1027 | letterSpacing: letterSpacing == null |
| 1028 | ? null |
| 1029 | : letterSpacing! * letterSpacingFactor + letterSpacingDelta, |
| 1030 | wordSpacing: wordSpacing == null ? null : wordSpacing! * wordSpacingFactor + wordSpacingDelta, |
| 1031 | textBaseline: textBaseline ?? this.textBaseline, |
| 1032 | height: (height == null || height == ui.kTextHeightNone) |
| 1033 | ? height |
| 1034 | : height! * heightFactor + heightDelta, |
| 1035 | leadingDistribution: leadingDistribution ?? this.leadingDistribution, |
| 1036 | locale: locale ?? this.locale, |
| 1037 | foreground: foreground, |
| 1038 | background: background, |
| 1039 | shadows: shadows ?? this.shadows, |
| 1040 | fontFeatures: fontFeatures ?? this.fontFeatures, |
| 1041 | fontVariations: fontVariations ?? this.fontVariations, |
| 1042 | decoration: decoration ?? this.decoration, |
| 1043 | decorationColor: decorationColor ?? this.decorationColor, |
| 1044 | decorationStyle: decorationStyle ?? this.decorationStyle, |
| 1045 | decorationThickness: decorationThickness == null |
| 1046 | ? null |
| 1047 | : decorationThickness! * decorationThicknessFactor + decorationThicknessDelta, |
| 1048 | overflow: overflow ?? this.overflow, |
| 1049 | package: package ?? _package, |
| 1050 | debugLabel: modifiedDebugLabel, |
| 1051 | ); |
| 1052 | } |
| 1053 | |
| 1054 | /// Returns a new text style that is a combination of this style and the given |
| 1055 | /// [other] style. |
| 1056 | /// |
| 1057 | /// If the given [other] text style has its [TextStyle.inherit] set to true, |
| 1058 | /// its null properties are replaced with the non-null properties of this text |
| 1059 | /// style. The [other] style _inherits_ the properties of this style. Another |
| 1060 | /// way to think of it is that the "missing" properties of the [other] style |
| 1061 | /// are _filled_ by the properties of this style. |
| 1062 | /// |
| 1063 | /// If the given [other] text style has its [TextStyle.inherit] set to false, |
| 1064 | /// returns the given [other] style unchanged. The [other] style does not |
| 1065 | /// inherit properties of this style. |
| 1066 | /// |
| 1067 | /// If the given text style is null, returns this text style. |
| 1068 | /// |
| 1069 | /// One of [color] or [foreground] must be null, and if this or `other` has |
| 1070 | /// [foreground] specified it will be given preference over any color parameter. |
| 1071 | /// |
| 1072 | /// Similarly, one of [backgroundColor] or [background] must be null, and if |
| 1073 | /// this or `other` has [background] specified it will be given preference |
| 1074 | /// over any backgroundColor parameter. |
| 1075 | TextStyle merge(TextStyle? other) { |
| 1076 | if (other == null) { |
| 1077 | return this; |
| 1078 | } |
| 1079 | if (!other.inherit) { |
| 1080 | return other; |
| 1081 | } |
| 1082 | |
| 1083 | String? mergedDebugLabel; |
| 1084 | assert(() { |
| 1085 | if (other.debugLabel != null || debugLabel != null) { |
| 1086 | mergedDebugLabel = |
| 1087 | '( ${debugLabel ?? _kDefaultDebugLabel}).merge( ${other.debugLabel ?? _kDefaultDebugLabel})' ; |
| 1088 | } |
| 1089 | return true; |
| 1090 | }()); |
| 1091 | |
| 1092 | return copyWith( |
| 1093 | color: other.color, |
| 1094 | backgroundColor: other.backgroundColor, |
| 1095 | fontSize: other.fontSize, |
| 1096 | fontWeight: other.fontWeight, |
| 1097 | fontStyle: other.fontStyle, |
| 1098 | letterSpacing: other.letterSpacing, |
| 1099 | wordSpacing: other.wordSpacing, |
| 1100 | textBaseline: other.textBaseline, |
| 1101 | height: other.height, |
| 1102 | leadingDistribution: other.leadingDistribution, |
| 1103 | locale: other.locale, |
| 1104 | foreground: other.foreground, |
| 1105 | background: other.background, |
| 1106 | shadows: other.shadows, |
| 1107 | fontFeatures: other.fontFeatures, |
| 1108 | fontVariations: other.fontVariations, |
| 1109 | decoration: other.decoration, |
| 1110 | decorationColor: other.decorationColor, |
| 1111 | decorationStyle: other.decorationStyle, |
| 1112 | decorationThickness: other.decorationThickness, |
| 1113 | debugLabel: mergedDebugLabel, |
| 1114 | fontFamily: other._fontFamily, |
| 1115 | fontFamilyFallback: other._fontFamilyFallback, |
| 1116 | package: other._package, |
| 1117 | overflow: other.overflow, |
| 1118 | ); |
| 1119 | } |
| 1120 | |
| 1121 | /// Interpolate between two text styles for animated transitions. |
| 1122 | /// |
| 1123 | /// Interpolation will not work well if the styles don't specify the same fields. |
| 1124 | /// When this happens, to keep the interpolated transition smooth, the |
| 1125 | /// implementation uses the non-null value throughout the transition for |
| 1126 | /// lerpable fields such as colors (for example, if one [TextStyle] specified |
| 1127 | /// `fontSize` but the other didn't, the returned [TextStyle] will use the |
| 1128 | /// `fontSize` from the [TextStyle] that specified it, regardless of the `t` |
| 1129 | /// value). |
| 1130 | /// |
| 1131 | /// This method throws when the given [TextStyle]s don't have the same |
| 1132 | /// [inherit] value and a lerpable field is missing from both [TextStyle]s, |
| 1133 | /// as that could result in jumpy transitions. |
| 1134 | /// |
| 1135 | /// {@macro dart.ui.shadow.lerp} |
| 1136 | /// |
| 1137 | /// If [foreground] is specified on either of `a` or `b`, both will be treated |
| 1138 | /// as if they have a [foreground] paint (creating a new [Paint] if necessary |
| 1139 | /// based on the [color] property). |
| 1140 | /// |
| 1141 | /// If [background] is specified on either of `a` or `b`, both will be treated |
| 1142 | /// as if they have a [background] paint (creating a new [Paint] if necessary |
| 1143 | /// based on the [backgroundColor] property). |
| 1144 | static TextStyle? lerp(TextStyle? a, TextStyle? b, double t) { |
| 1145 | if (identical(a, b)) { |
| 1146 | return a; |
| 1147 | } |
| 1148 | String? lerpDebugLabel; |
| 1149 | assert(() { |
| 1150 | lerpDebugLabel = |
| 1151 | 'lerp( ${a?.debugLabel ?? _kDefaultDebugLabel} ⎯ ${t.toStringAsFixed(1)}→ ${b?.debugLabel ?? _kDefaultDebugLabel})' ; |
| 1152 | return true; |
| 1153 | }()); |
| 1154 | |
| 1155 | if (a == null) { |
| 1156 | return TextStyle( |
| 1157 | inherit: b!.inherit, |
| 1158 | color: Color.lerp(null, b.color, t), |
| 1159 | backgroundColor: Color.lerp(null, b.backgroundColor, t), |
| 1160 | fontSize: t < 0.5 ? null : b.fontSize, |
| 1161 | fontWeight: FontWeight.lerp(null, b.fontWeight, t), |
| 1162 | fontStyle: t < 0.5 ? null : b.fontStyle, |
| 1163 | letterSpacing: t < 0.5 ? null : b.letterSpacing, |
| 1164 | wordSpacing: t < 0.5 ? null : b.wordSpacing, |
| 1165 | textBaseline: t < 0.5 ? null : b.textBaseline, |
| 1166 | height: t < 0.5 ? null : b.height, |
| 1167 | leadingDistribution: t < 0.5 ? null : b.leadingDistribution, |
| 1168 | locale: t < 0.5 ? null : b.locale, |
| 1169 | foreground: t < 0.5 ? null : b.foreground, |
| 1170 | background: t < 0.5 ? null : b.background, |
| 1171 | shadows: t < 0.5 ? null : b.shadows, |
| 1172 | fontFeatures: t < 0.5 ? null : b.fontFeatures, |
| 1173 | fontVariations: lerpFontVariations(null, b.fontVariations, t), |
| 1174 | decoration: t < 0.5 ? null : b.decoration, |
| 1175 | decorationColor: Color.lerp(null, b.decorationColor, t), |
| 1176 | decorationStyle: t < 0.5 ? null : b.decorationStyle, |
| 1177 | decorationThickness: t < 0.5 ? null : b.decorationThickness, |
| 1178 | debugLabel: lerpDebugLabel, |
| 1179 | fontFamily: t < 0.5 ? null : b._fontFamily, |
| 1180 | fontFamilyFallback: t < 0.5 ? null : b._fontFamilyFallback, |
| 1181 | package: t < 0.5 ? null : b._package, |
| 1182 | overflow: t < 0.5 ? null : b.overflow, |
| 1183 | ); |
| 1184 | } |
| 1185 | |
| 1186 | if (b == null) { |
| 1187 | return TextStyle( |
| 1188 | inherit: a.inherit, |
| 1189 | color: Color.lerp(a.color, null, t), |
| 1190 | backgroundColor: Color.lerp(null, a.backgroundColor, t), |
| 1191 | fontSize: t < 0.5 ? a.fontSize : null, |
| 1192 | fontWeight: FontWeight.lerp(a.fontWeight, null, t), |
| 1193 | fontStyle: t < 0.5 ? a.fontStyle : null, |
| 1194 | letterSpacing: t < 0.5 ? a.letterSpacing : null, |
| 1195 | wordSpacing: t < 0.5 ? a.wordSpacing : null, |
| 1196 | textBaseline: t < 0.5 ? a.textBaseline : null, |
| 1197 | height: t < 0.5 ? a.height : null, |
| 1198 | leadingDistribution: t < 0.5 ? a.leadingDistribution : null, |
| 1199 | locale: t < 0.5 ? a.locale : null, |
| 1200 | foreground: t < 0.5 ? a.foreground : null, |
| 1201 | background: t < 0.5 ? a.background : null, |
| 1202 | shadows: t < 0.5 ? a.shadows : null, |
| 1203 | fontFeatures: t < 0.5 ? a.fontFeatures : null, |
| 1204 | fontVariations: lerpFontVariations(a.fontVariations, null, t), |
| 1205 | decoration: t < 0.5 ? a.decoration : null, |
| 1206 | decorationColor: Color.lerp(a.decorationColor, null, t), |
| 1207 | decorationStyle: t < 0.5 ? a.decorationStyle : null, |
| 1208 | decorationThickness: t < 0.5 ? a.decorationThickness : null, |
| 1209 | debugLabel: lerpDebugLabel, |
| 1210 | fontFamily: t < 0.5 ? a._fontFamily : null, |
| 1211 | fontFamilyFallback: t < 0.5 ? a._fontFamilyFallback : null, |
| 1212 | package: t < 0.5 ? a._package : null, |
| 1213 | overflow: t < 0.5 ? a.overflow : null, |
| 1214 | ); |
| 1215 | } |
| 1216 | |
| 1217 | assert(() { |
| 1218 | if (a.inherit == b.inherit) { |
| 1219 | return true; |
| 1220 | } |
| 1221 | |
| 1222 | final List<String> nullFields = <String>[ |
| 1223 | if (a.foreground == null && b.foreground == null && a.color == null && b.color == null) |
| 1224 | 'color' , |
| 1225 | if (a.background == null && |
| 1226 | b.background == null && |
| 1227 | a.backgroundColor == null && |
| 1228 | b.backgroundColor == null) |
| 1229 | 'backgroundColor' , |
| 1230 | if (a.fontSize == null && b.fontSize == null) 'fontSize' , |
| 1231 | if (a.letterSpacing == null && b.letterSpacing == null) 'letterSpacing' , |
| 1232 | if (a.wordSpacing == null && b.wordSpacing == null) 'wordSpacing' , |
| 1233 | if (a.height == null && b.height == null) 'height' , |
| 1234 | if (a.decorationColor == null && b.decorationColor == null) 'decorationColor' , |
| 1235 | if (a.decorationThickness == null && b.decorationThickness == null) 'decorationThickness' , |
| 1236 | ]; |
| 1237 | if (nullFields.isEmpty) { |
| 1238 | return true; |
| 1239 | } |
| 1240 | |
| 1241 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
| 1242 | ErrorSummary('Failed to interpolate TextStyles with different inherit values.' ), |
| 1243 | ErrorSpacer(), |
| 1244 | ErrorDescription('The TextStyles being interpolated were:' ), |
| 1245 | a.toDiagnosticsNode(name: 'from' , style: DiagnosticsTreeStyle.singleLine), |
| 1246 | b.toDiagnosticsNode(name: 'to' , style: DiagnosticsTreeStyle.singleLine), |
| 1247 | ErrorDescription( |
| 1248 | 'The following fields are unspecified in both TextStyles:\n' |
| 1249 | ' ${nullFields.map((String name) => '" $name"' ).join(', ' )}.\n' |
| 1250 | 'When "inherit" changes during the transition, these fields may ' |
| 1251 | 'observe abrupt value changes as a result, causing "jump"s in the ' |
| 1252 | 'transition.' , |
| 1253 | ), |
| 1254 | ErrorSpacer(), |
| 1255 | ErrorHint( |
| 1256 | 'In general, TextStyle.lerp only works well when both TextStyles have ' |
| 1257 | 'the same "inherit" value, and specify the same fields.' , |
| 1258 | ), |
| 1259 | ErrorHint( |
| 1260 | 'If the TextStyles were directly created by you, consider bringing ' |
| 1261 | 'them to parity to ensure a smooth transition.' , |
| 1262 | ), |
| 1263 | ErrorSpacer(), |
| 1264 | ErrorHint( |
| 1265 | 'If one of the TextStyles being lerped is significantly more elaborate ' |
| 1266 | 'than the other, and has "inherited" set to false, it is often because ' |
| 1267 | 'it is merged with another TextStyle before being lerped. Comparing ' |
| 1268 | 'the "debugLabel"s of the two TextStyles may help identify if that was ' |
| 1269 | 'the case.' , |
| 1270 | ), |
| 1271 | ErrorHint( |
| 1272 | 'For example, you may see this error message when trying to lerp ' |
| 1273 | 'between "ThemeData()" and "Theme.of(context)". This is because ' |
| 1274 | 'TextStyles from "Theme.of(context)" are merged with TextStyles from ' |
| 1275 | 'another theme and thus are more elaborate than the TextStyles from ' |
| 1276 | '"ThemeData()" (which is reflected in their "debugLabel"s -- ' |
| 1277 | 'TextStyles from "Theme.of(context)" should have labels in the form of ' |
| 1278 | '"(<A TextStyle>).merge(<Another TextStyle>)"). It is recommended to ' |
| 1279 | 'only lerp ThemeData with matching TextStyles.' , |
| 1280 | ), |
| 1281 | ]); |
| 1282 | }()); |
| 1283 | |
| 1284 | return TextStyle( |
| 1285 | inherit: t < 0.5 ? a.inherit : b.inherit, |
| 1286 | color: a.foreground == null && b.foreground == null ? Color.lerp(a.color, b.color, t) : null, |
| 1287 | backgroundColor: a.background == null && b.background == null |
| 1288 | ? Color.lerp(a.backgroundColor, b.backgroundColor, t) |
| 1289 | : null, |
| 1290 | fontSize: ui.lerpDouble(a.fontSize ?? b.fontSize, b.fontSize ?? a.fontSize, t), |
| 1291 | fontWeight: FontWeight.lerp(a.fontWeight, b.fontWeight, t), |
| 1292 | fontStyle: t < 0.5 ? a.fontStyle : b.fontStyle, |
| 1293 | letterSpacing: ui.lerpDouble( |
| 1294 | a.letterSpacing ?? b.letterSpacing, |
| 1295 | b.letterSpacing ?? a.letterSpacing, |
| 1296 | t, |
| 1297 | ), |
| 1298 | wordSpacing: ui.lerpDouble(a.wordSpacing ?? b.wordSpacing, b.wordSpacing ?? a.wordSpacing, t), |
| 1299 | textBaseline: t < 0.5 ? a.textBaseline : b.textBaseline, |
| 1300 | height: ui.lerpDouble(a.height ?? b.height, b.height ?? a.height, t), |
| 1301 | leadingDistribution: t < 0.5 ? a.leadingDistribution : b.leadingDistribution, |
| 1302 | locale: t < 0.5 ? a.locale : b.locale, |
| 1303 | foreground: (a.foreground != null || b.foreground != null) |
| 1304 | ? t < 0.5 |
| 1305 | ? a.foreground ?? (Paint()..color = a.color!) |
| 1306 | : b.foreground ?? (Paint()..color = b.color!) |
| 1307 | : null, |
| 1308 | background: (a.background != null || b.background != null) |
| 1309 | ? t < 0.5 |
| 1310 | ? a.background ?? (Paint()..color = a.backgroundColor!) |
| 1311 | : b.background ?? (Paint()..color = b.backgroundColor!) |
| 1312 | : null, |
| 1313 | shadows: ui.Shadow.lerpList(a.shadows, b.shadows, t), |
| 1314 | fontFeatures: t < 0.5 ? a.fontFeatures : b.fontFeatures, |
| 1315 | fontVariations: lerpFontVariations(a.fontVariations, b.fontVariations, t), |
| 1316 | decoration: t < 0.5 ? a.decoration : b.decoration, |
| 1317 | decorationColor: Color.lerp(a.decorationColor, b.decorationColor, t), |
| 1318 | decorationStyle: t < 0.5 ? a.decorationStyle : b.decorationStyle, |
| 1319 | decorationThickness: ui.lerpDouble( |
| 1320 | a.decorationThickness ?? b.decorationThickness, |
| 1321 | b.decorationThickness ?? a.decorationThickness, |
| 1322 | t, |
| 1323 | ), |
| 1324 | debugLabel: lerpDebugLabel, |
| 1325 | fontFamily: t < 0.5 ? a._fontFamily : b._fontFamily, |
| 1326 | fontFamilyFallback: t < 0.5 ? a._fontFamilyFallback : b._fontFamilyFallback, |
| 1327 | package: t < 0.5 ? a._package : b._package, |
| 1328 | overflow: t < 0.5 ? a.overflow : b.overflow, |
| 1329 | ); |
| 1330 | } |
| 1331 | |
| 1332 | /// The style information for text runs, encoded for use by `dart:ui`. |
| 1333 | ui.TextStyle getTextStyle({ |
| 1334 | @Deprecated( |
| 1335 | 'Use textScaler instead. ' |
| 1336 | 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. ' |
| 1337 | 'This feature was deprecated after v3.12.0-2.0.pre.' , |
| 1338 | ) |
| 1339 | double textScaleFactor = 1.0, |
| 1340 | TextScaler textScaler = TextScaler.noScaling, |
| 1341 | }) { |
| 1342 | assert( |
| 1343 | identical(textScaler, TextScaler.noScaling) || textScaleFactor == 1.0, |
| 1344 | 'textScaleFactor is deprecated and cannot be specified when textScaler is specified.' , |
| 1345 | ); |
| 1346 | final double? fontSize = switch (this.fontSize) { |
| 1347 | null => null, |
| 1348 | final double size when textScaler == TextScaler.noScaling => size * textScaleFactor, |
| 1349 | final double size => textScaler.scale(size), |
| 1350 | }; |
| 1351 | return ui.TextStyle( |
| 1352 | color: color, |
| 1353 | decoration: decoration, |
| 1354 | decorationColor: decorationColor, |
| 1355 | decorationStyle: decorationStyle, |
| 1356 | decorationThickness: decorationThickness, |
| 1357 | fontWeight: fontWeight, |
| 1358 | fontStyle: fontStyle, |
| 1359 | textBaseline: textBaseline, |
| 1360 | leadingDistribution: leadingDistribution, |
| 1361 | fontFamily: fontFamily, |
| 1362 | fontFamilyFallback: fontFamilyFallback, |
| 1363 | fontSize: fontSize, |
| 1364 | letterSpacing: letterSpacing, |
| 1365 | wordSpacing: wordSpacing, |
| 1366 | height: height, |
| 1367 | locale: locale, |
| 1368 | foreground: foreground, |
| 1369 | background: switch ((background, backgroundColor)) { |
| 1370 | (final Paint paint, _) => paint, |
| 1371 | (_, final Color color) => Paint()..color = color, |
| 1372 | _ => null, |
| 1373 | }, |
| 1374 | shadows: shadows, |
| 1375 | fontFeatures: fontFeatures, |
| 1376 | fontVariations: fontVariations, |
| 1377 | ); |
| 1378 | } |
| 1379 | |
| 1380 | /// The style information for paragraphs, encoded for use by `dart:ui`. |
| 1381 | /// |
| 1382 | /// If the `textScaleFactor` argument is omitted, it defaults to one. The |
| 1383 | /// other arguments may be null. The `maxLines` argument, if specified and |
| 1384 | /// non-null, must be greater than zero. |
| 1385 | /// |
| 1386 | /// If the font size on this style isn't set, it will default to 14 logical |
| 1387 | /// pixels. |
| 1388 | ui.ParagraphStyle getParagraphStyle({ |
| 1389 | TextAlign? textAlign, |
| 1390 | TextDirection? textDirection, |
| 1391 | TextScaler textScaler = TextScaler.noScaling, |
| 1392 | String? ellipsis, |
| 1393 | int? maxLines, |
| 1394 | TextHeightBehavior? textHeightBehavior, |
| 1395 | Locale? locale, |
| 1396 | String? fontFamily, |
| 1397 | double? fontSize, |
| 1398 | FontWeight? fontWeight, |
| 1399 | FontStyle? fontStyle, |
| 1400 | double? height, |
| 1401 | StrutStyle? strutStyle, |
| 1402 | }) { |
| 1403 | assert(maxLines == null || maxLines > 0); |
| 1404 | final TextLeadingDistribution? leadingDistribution = this.leadingDistribution; |
| 1405 | final TextHeightBehavior? effectiveTextHeightBehavior = |
| 1406 | textHeightBehavior ?? |
| 1407 | (leadingDistribution == null |
| 1408 | ? null |
| 1409 | : TextHeightBehavior(leadingDistribution: leadingDistribution)); |
| 1410 | |
| 1411 | return ui.ParagraphStyle( |
| 1412 | textAlign: textAlign, |
| 1413 | textDirection: textDirection, |
| 1414 | // Here, we establish the contents of this TextStyle as the paragraph's default font |
| 1415 | // unless an override is passed in. |
| 1416 | fontWeight: fontWeight ?? this.fontWeight, |
| 1417 | fontStyle: fontStyle ?? this.fontStyle, |
| 1418 | fontFamily: fontFamily ?? this.fontFamily, |
| 1419 | fontSize: textScaler.scale(fontSize ?? this.fontSize ?? kDefaultFontSize), |
| 1420 | height: height ?? this.height, |
| 1421 | textHeightBehavior: effectiveTextHeightBehavior, |
| 1422 | strutStyle: strutStyle == null |
| 1423 | ? null |
| 1424 | : ui.StrutStyle( |
| 1425 | fontFamily: strutStyle.fontFamily, |
| 1426 | fontFamilyFallback: strutStyle.fontFamilyFallback, |
| 1427 | fontSize: switch (strutStyle.fontSize) { |
| 1428 | null => null, |
| 1429 | final double unscaled => textScaler.scale(unscaled), |
| 1430 | }, |
| 1431 | height: strutStyle.height, |
| 1432 | leading: strutStyle.leading, |
| 1433 | leadingDistribution: strutStyle.leadingDistribution, |
| 1434 | fontWeight: strutStyle.fontWeight, |
| 1435 | fontStyle: strutStyle.fontStyle, |
| 1436 | forceStrutHeight: strutStyle.forceStrutHeight, |
| 1437 | ), |
| 1438 | maxLines: maxLines, |
| 1439 | ellipsis: ellipsis, |
| 1440 | locale: locale, |
| 1441 | ); |
| 1442 | } |
| 1443 | |
| 1444 | /// Describe the difference between this style and another, in terms of how |
| 1445 | /// much damage it will make to the rendering. |
| 1446 | /// |
| 1447 | /// See also: |
| 1448 | /// |
| 1449 | /// * [TextSpan.compareTo], which does the same thing for entire [TextSpan]s. |
| 1450 | RenderComparison compareTo(TextStyle other) { |
| 1451 | if (identical(this, other)) { |
| 1452 | return RenderComparison.identical; |
| 1453 | } |
| 1454 | if (inherit != other.inherit || |
| 1455 | fontFamily != other.fontFamily || |
| 1456 | fontSize != other.fontSize || |
| 1457 | fontWeight != other.fontWeight || |
| 1458 | fontStyle != other.fontStyle || |
| 1459 | letterSpacing != other.letterSpacing || |
| 1460 | wordSpacing != other.wordSpacing || |
| 1461 | textBaseline != other.textBaseline || |
| 1462 | height != other.height || |
| 1463 | leadingDistribution != other.leadingDistribution || |
| 1464 | locale != other.locale || |
| 1465 | foreground != other.foreground || |
| 1466 | background != other.background || |
| 1467 | !listEquals(shadows, other.shadows) || |
| 1468 | !listEquals(fontFeatures, other.fontFeatures) || |
| 1469 | !listEquals(fontVariations, other.fontVariations) || |
| 1470 | !listEquals(fontFamilyFallback, other.fontFamilyFallback) || |
| 1471 | overflow != other.overflow) { |
| 1472 | return RenderComparison.layout; |
| 1473 | } |
| 1474 | if (color != other.color || |
| 1475 | backgroundColor != other.backgroundColor || |
| 1476 | decoration != other.decoration || |
| 1477 | decorationColor != other.decorationColor || |
| 1478 | decorationStyle != other.decorationStyle || |
| 1479 | decorationThickness != other.decorationThickness) { |
| 1480 | return RenderComparison.paint; |
| 1481 | } |
| 1482 | return RenderComparison.identical; |
| 1483 | } |
| 1484 | |
| 1485 | @override |
| 1486 | bool operator ==(Object other) { |
| 1487 | if (identical(this, other)) { |
| 1488 | return true; |
| 1489 | } |
| 1490 | if (other.runtimeType != runtimeType) { |
| 1491 | return false; |
| 1492 | } |
| 1493 | return other is TextStyle && |
| 1494 | other.inherit == inherit && |
| 1495 | other.color == color && |
| 1496 | other.backgroundColor == backgroundColor && |
| 1497 | other.fontSize == fontSize && |
| 1498 | other.fontWeight == fontWeight && |
| 1499 | other.fontStyle == fontStyle && |
| 1500 | other.letterSpacing == letterSpacing && |
| 1501 | other.wordSpacing == wordSpacing && |
| 1502 | other.textBaseline == textBaseline && |
| 1503 | other.height == height && |
| 1504 | other.leadingDistribution == leadingDistribution && |
| 1505 | other.locale == locale && |
| 1506 | other.foreground == foreground && |
| 1507 | other.background == background && |
| 1508 | listEquals(other.shadows, shadows) && |
| 1509 | listEquals(other.fontFeatures, fontFeatures) && |
| 1510 | listEquals(other.fontVariations, fontVariations) && |
| 1511 | other.decoration == decoration && |
| 1512 | other.decorationColor == decorationColor && |
| 1513 | other.decorationStyle == decorationStyle && |
| 1514 | other.decorationThickness == decorationThickness && |
| 1515 | other.fontFamily == fontFamily && |
| 1516 | listEquals(other.fontFamilyFallback, fontFamilyFallback) && |
| 1517 | other._package == _package && |
| 1518 | other.overflow == overflow; |
| 1519 | } |
| 1520 | |
| 1521 | @override |
| 1522 | int get hashCode { |
| 1523 | final List<String>? fontFamilyFallback = this.fontFamilyFallback; |
| 1524 | final int fontHash = Object.hash( |
| 1525 | decorationStyle, |
| 1526 | decorationThickness, |
| 1527 | fontFamily, |
| 1528 | fontFamilyFallback == null ? null : Object.hashAll(fontFamilyFallback), |
| 1529 | _package, |
| 1530 | overflow, |
| 1531 | ); |
| 1532 | |
| 1533 | final List<Shadow>? shadows = this.shadows; |
| 1534 | final List<FontFeature>? fontFeatures = this.fontFeatures; |
| 1535 | final List<FontVariation>? fontVariations = this.fontVariations; |
| 1536 | return Object.hash( |
| 1537 | inherit, |
| 1538 | color, |
| 1539 | backgroundColor, |
| 1540 | fontSize, |
| 1541 | fontWeight, |
| 1542 | fontStyle, |
| 1543 | letterSpacing, |
| 1544 | wordSpacing, |
| 1545 | textBaseline, |
| 1546 | height, |
| 1547 | leadingDistribution, |
| 1548 | locale, |
| 1549 | foreground, |
| 1550 | background, |
| 1551 | shadows == null ? null : Object.hashAll(shadows), |
| 1552 | fontFeatures == null ? null : Object.hashAll(fontFeatures), |
| 1553 | fontVariations == null ? null : Object.hashAll(fontVariations), |
| 1554 | decoration, |
| 1555 | decorationColor, |
| 1556 | fontHash, |
| 1557 | ); |
| 1558 | } |
| 1559 | |
| 1560 | @override |
| 1561 | String toStringShort() => objectRuntimeType(this, 'TextStyle' ); |
| 1562 | |
| 1563 | /// Adds all properties prefixing property names with the optional `prefix`. |
| 1564 | @override |
| 1565 | void debugFillProperties(DiagnosticPropertiesBuilder properties, {String prefix = '' }) { |
| 1566 | super.debugFillProperties(properties); |
| 1567 | if (debugLabel != null) { |
| 1568 | properties.add(MessageProperty(' ${prefix}debugLabel' , debugLabel!)); |
| 1569 | } |
| 1570 | final List<DiagnosticsNode> styles = <DiagnosticsNode>[ |
| 1571 | ColorProperty(' ${prefix}color' , color, defaultValue: null), |
| 1572 | ColorProperty(' ${prefix}backgroundColor' , backgroundColor, defaultValue: null), |
| 1573 | StringProperty(' ${prefix}family' , fontFamily, defaultValue: null, quoted: false), |
| 1574 | IterableProperty<String>(' ${prefix}familyFallback' , fontFamilyFallback, defaultValue: null), |
| 1575 | DoubleProperty(' ${prefix}size' , fontSize, defaultValue: null), |
| 1576 | ]; |
| 1577 | String? weightDescription; |
| 1578 | if (fontWeight != null) { |
| 1579 | weightDescription = ' ${fontWeight!.index + 1}00' ; |
| 1580 | } |
| 1581 | // TODO(jacobr): switch this to use enumProperty which will either cause the |
| 1582 | // weight description to change to w600 from 600 or require existing |
| 1583 | // enumProperty to handle this special case. |
| 1584 | styles.add( |
| 1585 | DiagnosticsProperty<FontWeight>( |
| 1586 | ' ${prefix}weight' , |
| 1587 | fontWeight, |
| 1588 | description: weightDescription, |
| 1589 | defaultValue: null, |
| 1590 | ), |
| 1591 | ); |
| 1592 | styles.add(EnumProperty<FontStyle>(' ${prefix}style' , fontStyle, defaultValue: null)); |
| 1593 | styles.add(DoubleProperty(' ${prefix}letterSpacing' , letterSpacing, defaultValue: null)); |
| 1594 | styles.add(DoubleProperty(' ${prefix}wordSpacing' , wordSpacing, defaultValue: null)); |
| 1595 | styles.add(EnumProperty<TextBaseline>(' ${prefix}baseline' , textBaseline, defaultValue: null)); |
| 1596 | styles.add(DoubleProperty(' ${prefix}height' , height, unit: 'x' , defaultValue: null)); |
| 1597 | styles.add( |
| 1598 | EnumProperty<TextLeadingDistribution>( |
| 1599 | ' ${prefix}leadingDistribution' , |
| 1600 | leadingDistribution, |
| 1601 | defaultValue: null, |
| 1602 | ), |
| 1603 | ); |
| 1604 | styles.add(DiagnosticsProperty<Locale>(' ${prefix}locale' , locale, defaultValue: null)); |
| 1605 | styles.add(DiagnosticsProperty<Paint>(' ${prefix}foreground' , foreground, defaultValue: null)); |
| 1606 | styles.add(DiagnosticsProperty<Paint>(' ${prefix}background' , background, defaultValue: null)); |
| 1607 | if (decoration != null || |
| 1608 | decorationColor != null || |
| 1609 | decorationStyle != null || |
| 1610 | decorationThickness != null) { |
| 1611 | final List<String> decorationDescription = <String>[]; |
| 1612 | if (decorationStyle != null) { |
| 1613 | decorationDescription.add(decorationStyle!.name); |
| 1614 | } |
| 1615 | |
| 1616 | // Hide decorationColor from the default text view as it is shown in the |
| 1617 | // terse decoration summary as well. |
| 1618 | styles.add( |
| 1619 | ColorProperty( |
| 1620 | ' ${prefix}decorationColor' , |
| 1621 | decorationColor, |
| 1622 | defaultValue: null, |
| 1623 | level: DiagnosticLevel.fine, |
| 1624 | ), |
| 1625 | ); |
| 1626 | |
| 1627 | if (decorationColor != null) { |
| 1628 | decorationDescription.add(' $decorationColor' ); |
| 1629 | } |
| 1630 | |
| 1631 | // Intentionally collide with the property 'decoration' added below. |
| 1632 | // Tools that show hidden properties could choose the first property |
| 1633 | // matching the name to disambiguate. |
| 1634 | styles.add( |
| 1635 | DiagnosticsProperty<TextDecoration>( |
| 1636 | ' ${prefix}decoration' , |
| 1637 | decoration, |
| 1638 | defaultValue: null, |
| 1639 | level: DiagnosticLevel.hidden, |
| 1640 | ), |
| 1641 | ); |
| 1642 | if (decoration != null) { |
| 1643 | decorationDescription.add(' $decoration' ); |
| 1644 | } |
| 1645 | assert(decorationDescription.isNotEmpty); |
| 1646 | styles.add(MessageProperty(' ${prefix}decoration' , decorationDescription.join(' ' ))); |
| 1647 | styles.add( |
| 1648 | DoubleProperty( |
| 1649 | ' ${prefix}decorationThickness' , |
| 1650 | decorationThickness, |
| 1651 | unit: 'x' , |
| 1652 | defaultValue: null, |
| 1653 | ), |
| 1654 | ); |
| 1655 | } |
| 1656 | |
| 1657 | final bool styleSpecified = styles.any( |
| 1658 | (DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info), |
| 1659 | ); |
| 1660 | properties.add( |
| 1661 | DiagnosticsProperty<bool>( |
| 1662 | ' ${prefix}inherit' , |
| 1663 | inherit, |
| 1664 | level: (!styleSpecified && inherit) ? DiagnosticLevel.fine : DiagnosticLevel.info, |
| 1665 | ), |
| 1666 | ); |
| 1667 | styles.forEach(properties.add); |
| 1668 | |
| 1669 | if (!styleSpecified) { |
| 1670 | properties.add( |
| 1671 | FlagProperty( |
| 1672 | 'inherit' , |
| 1673 | value: inherit, |
| 1674 | ifTrue: ' $prefix<all styles inherited>' , |
| 1675 | ifFalse: ' $prefix<no style specified>' , |
| 1676 | ), |
| 1677 | ); |
| 1678 | } |
| 1679 | |
| 1680 | styles.add(EnumProperty<TextOverflow>(' ${prefix}overflow' , overflow, defaultValue: null)); |
| 1681 | } |
| 1682 | } |
| 1683 | |
| 1684 | /// Interpolate between two lists of [FontVariation] objects. |
| 1685 | /// |
| 1686 | /// Variations are paired by axis, and interpolated using [FontVariation.lerp]. |
| 1687 | /// |
| 1688 | /// Entries that are only present in one list are animated using a step-function |
| 1689 | /// at t=0.5 that enables or disables the variation. This can be jarring and |
| 1690 | /// largely defeats the point of animating font variations. For best results, |
| 1691 | /// specify the same axes in both lists, and for best performance, specify them |
| 1692 | /// in the same order. |
| 1693 | /// |
| 1694 | /// ## Performance details |
| 1695 | /// |
| 1696 | /// This algorithm is O(N), but the constant factor varies based on the input, |
| 1697 | /// and that is probably more important (because typically N is going to be |
| 1698 | /// tiny, like 1 or 2; at the time of writing, there are only about five defined |
| 1699 | /// axes that fonts typically use!). |
| 1700 | /// |
| 1701 | /// It is fastest when the lists contain the same axes ([FontVariation.axis]) in |
| 1702 | /// the same order. The result is again in the same order, and no attempt is |
| 1703 | /// made to detect or remove duplicates in this process. This is, by far, the |
| 1704 | /// recommended way to use this algorithm. |
| 1705 | /// |
| 1706 | /// When the order of items in the two input lists vary, the constant factor |
| 1707 | /// increases substantially, as it involves creating two maps and a set, |
| 1708 | /// inserting every variation in both lists into the maps and the set, and then |
| 1709 | /// iterating over them to recreate the list. |
| 1710 | /// |
| 1711 | /// In this case, the resulting order is arbitrary. Duplicates are dropped; in |
| 1712 | /// each list, the last [FontVariation] for any particular axis is the one used |
| 1713 | /// to compute the value for that axis. Values that only appear on one side are |
| 1714 | /// interpolated using [FontVariation.lerp] against a null value, and resulting |
| 1715 | /// null values are omitted from the resulting list. |
| 1716 | /// |
| 1717 | /// When the lists begin with matching pairs of axes, the fast algorithm is used |
| 1718 | /// up to the point where the lists diverge, and the more expensive algorithm |
| 1719 | /// is used on the remaining entries. |
| 1720 | /// |
| 1721 | /// See also: |
| 1722 | /// |
| 1723 | /// * [TextStyle.lerp], which uses this function to handle |
| 1724 | /// [TextStyle.fontVariations]. |
| 1725 | List<FontVariation>? lerpFontVariations(List<FontVariation>? a, List<FontVariation>? b, double t) { |
| 1726 | if (t == 0.0) { |
| 1727 | return a; |
| 1728 | } |
| 1729 | if (t == 1.0) { |
| 1730 | return b; |
| 1731 | } |
| 1732 | if (a == null || a.isEmpty || b == null || b.isEmpty) { |
| 1733 | // If one side is empty, that means anything on the other |
| 1734 | // side will use the null-to-something lerp, which is to |
| 1735 | // say, a step function at t=0.5. |
| 1736 | return t < 0.5 ? a : b; |
| 1737 | } |
| 1738 | assert(a.isNotEmpty && b.isNotEmpty); |
| 1739 | final List<FontVariation> result = <FontVariation>[]; |
| 1740 | // First, try the efficient O(N) solution in the event that |
| 1741 | // the lists are compatible. |
| 1742 | int index = 0; |
| 1743 | final int minLength = a.length < b.length ? a.length : b.length; |
| 1744 | for (; index < minLength; index += 1) { |
| 1745 | if (a[index].axis != b[index].axis) { |
| 1746 | // The lists aren't compatible. |
| 1747 | break; |
| 1748 | } |
| 1749 | result.add(FontVariation.lerp(a[index], b[index], t)!); |
| 1750 | } |
| 1751 | final int maxLength = a.length > b.length ? a.length : b.length; |
| 1752 | if (index < maxLength) { |
| 1753 | // If we get here, we have found some case where we cannot |
| 1754 | // use the efficient approach. |
| 1755 | final Set<String> axes = HashSet<String>(); |
| 1756 | final Map<String, FontVariation> aVariations = HashMap<String, FontVariation>(); |
| 1757 | for (int indexA = index; indexA < a.length; indexA += 1) { |
| 1758 | aVariations[a[indexA].axis] = a[indexA]; |
| 1759 | axes.add(a[indexA].axis); |
| 1760 | } |
| 1761 | final Map<String, FontVariation> bVariations = HashMap<String, FontVariation>(); |
| 1762 | for (int indexB = index; indexB < b.length; indexB += 1) { |
| 1763 | bVariations[b[indexB].axis] = b[indexB]; |
| 1764 | axes.add(b[indexB].axis); |
| 1765 | } |
| 1766 | for (final String axis in axes) { |
| 1767 | final FontVariation? variation = FontVariation.lerp(aVariations[axis], bVariations[axis], t); |
| 1768 | if (variation != null) { |
| 1769 | result.add(variation); |
| 1770 | } |
| 1771 | } |
| 1772 | } |
| 1773 | return result; |
| 1774 | } |
| 1775 | |