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
5import 'dart:ui' as ui show TextHeightBehavior;
6
7import 'package:flutter/rendering.dart';
8
9import 'basic.dart';
10import 'default_selection_style.dart';
11import 'framework.dart';
12import 'inherited_theme.dart';
13import 'media_query.dart';
14import 'selection_container.dart';
15
16// Examples can assume:
17// late String _name;
18// late BuildContext context;
19
20/// The text style to apply to descendant [Text] widgets which don't have an
21/// explicit style.
22///
23/// {@tool dartpad}
24/// This example shows how to use [DefaultTextStyle.merge] to create a default
25/// text style that inherits styling information from the current default text
26/// style and overrides some properties.
27///
28/// ** See code in examples/api/lib/widgets/text/text.0.dart **
29/// {@end-tool}
30///
31/// See also:
32///
33/// * [AnimatedDefaultTextStyle], which animates changes in the text style
34/// smoothly over a given duration.
35/// * [DefaultTextStyleTransition], which takes a provided [Animation] to
36/// animate changes in text style smoothly over time.
37class DefaultTextStyle extends InheritedTheme {
38 /// Creates a default text style for the given subtree.
39 ///
40 /// Consider using [DefaultTextStyle.merge] to inherit styling information
41 /// from the current default text style for a given [BuildContext].
42 ///
43 /// The [maxLines] property may be null (and indeed defaults to null), but if
44 /// it is not null, it must be greater than zero.
45 const DefaultTextStyle({
46 super.key,
47 required this.style,
48 this.textAlign,
49 this.softWrap = true,
50 this.overflow = TextOverflow.clip,
51 this.maxLines,
52 this.textWidthBasis = TextWidthBasis.parent,
53 this.textHeightBehavior,
54 required super.child,
55 }) : assert(maxLines == null || maxLines > 0);
56
57 /// A const-constructable default text style that provides fallback values.
58 ///
59 /// Returned from [of] when the given [BuildContext] doesn't have an enclosing default text style.
60 ///
61 /// This constructor creates a [DefaultTextStyle] with an invalid [child], which
62 /// means the constructed value cannot be incorporated into the tree.
63 const DefaultTextStyle.fallback({ super.key })
64 : style = const TextStyle(),
65 textAlign = null,
66 softWrap = true,
67 maxLines = null,
68 overflow = TextOverflow.clip,
69 textWidthBasis = TextWidthBasis.parent,
70 textHeightBehavior = null,
71 super(child: const _NullWidget());
72
73 /// Creates a default text style that overrides the text styles in scope at
74 /// this point in the widget tree.
75 ///
76 /// The given [style] is merged with the [style] from the default text style
77 /// for the [BuildContext] where the widget is inserted, and any of the other
78 /// arguments that are not null replace the corresponding properties on that
79 /// same default text style.
80 ///
81 /// This constructor cannot be used to override the [maxLines] property of the
82 /// ancestor with the value null, since null here is used to mean "defer to
83 /// ancestor". To replace a non-null [maxLines] from an ancestor with the null
84 /// value (to remove the restriction on number of lines), manually obtain the
85 /// ambient [DefaultTextStyle] using [DefaultTextStyle.of], then create a new
86 /// [DefaultTextStyle] using the [DefaultTextStyle.new] constructor directly.
87 /// See the source below for an example of how to do this (since that's
88 /// essentially what this constructor does).
89 static Widget merge({
90 Key? key,
91 TextStyle? style,
92 TextAlign? textAlign,
93 bool? softWrap,
94 TextOverflow? overflow,
95 int? maxLines,
96 TextWidthBasis? textWidthBasis,
97 required Widget child,
98 }) {
99 return Builder(
100 builder: (BuildContext context) {
101 final DefaultTextStyle parent = DefaultTextStyle.of(context);
102 return DefaultTextStyle(
103 key: key,
104 style: parent.style.merge(style),
105 textAlign: textAlign ?? parent.textAlign,
106 softWrap: softWrap ?? parent.softWrap,
107 overflow: overflow ?? parent.overflow,
108 maxLines: maxLines ?? parent.maxLines,
109 textWidthBasis: textWidthBasis ?? parent.textWidthBasis,
110 child: child,
111 );
112 },
113 );
114 }
115
116 /// The text style to apply.
117 final TextStyle style;
118
119 /// How each line of text in the Text widget should be aligned horizontally.
120 final TextAlign? textAlign;
121
122 /// Whether the text should break at soft line breaks.
123 ///
124 /// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
125 ///
126 /// This also decides the [overflow] property's behavior. If this is true or null,
127 /// the glyph causing overflow, and those that follow, will not be rendered.
128 final bool softWrap;
129
130 /// How visual overflow should be handled.
131 ///
132 /// If [softWrap] is true or null, the glyph causing overflow, and those that follow,
133 /// will not be rendered. Otherwise, it will be shown with the given overflow option.
134 final TextOverflow overflow;
135
136 /// An optional maximum number of lines for the text to span, wrapping if necessary.
137 /// If the text exceeds the given number of lines, it will be truncated according
138 /// to [overflow].
139 ///
140 /// If this is 1, text will not wrap. Otherwise, text will be wrapped at the
141 /// edge of the box.
142 ///
143 /// If this is non-null, it will override even explicit null values of
144 /// [Text.maxLines].
145 final int? maxLines;
146
147 /// The strategy to use when calculating the width of the Text.
148 ///
149 /// See [TextWidthBasis] for possible values and their implications.
150 final TextWidthBasis textWidthBasis;
151
152 /// {@macro dart.ui.textHeightBehavior}
153 final ui.TextHeightBehavior? textHeightBehavior;
154
155 /// The closest instance of this class that encloses the given context.
156 ///
157 /// If no such instance exists, returns an instance created by
158 /// [DefaultTextStyle.fallback], which contains fallback values.
159 ///
160 /// Typical usage is as follows:
161 ///
162 /// ```dart
163 /// DefaultTextStyle style = DefaultTextStyle.of(context);
164 /// ```
165 static DefaultTextStyle of(BuildContext context) {
166 return context.dependOnInheritedWidgetOfExactType<DefaultTextStyle>() ?? const DefaultTextStyle.fallback();
167 }
168
169 @override
170 bool updateShouldNotify(DefaultTextStyle oldWidget) {
171 return style != oldWidget.style ||
172 textAlign != oldWidget.textAlign ||
173 softWrap != oldWidget.softWrap ||
174 overflow != oldWidget.overflow ||
175 maxLines != oldWidget.maxLines ||
176 textWidthBasis != oldWidget.textWidthBasis ||
177 textHeightBehavior != oldWidget.textHeightBehavior;
178 }
179
180 @override
181 Widget wrap(BuildContext context, Widget child) {
182 return DefaultTextStyle(
183 style: style,
184 textAlign: textAlign,
185 softWrap: softWrap,
186 overflow: overflow,
187 maxLines: maxLines,
188 textWidthBasis: textWidthBasis,
189 textHeightBehavior: textHeightBehavior,
190 child: child,
191 );
192 }
193
194 @override
195 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
196 super.debugFillProperties(properties);
197 style.debugFillProperties(properties);
198 properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
199 properties.add(FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
200 properties.add(EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
201 properties.add(IntProperty('maxLines', maxLines, defaultValue: null));
202 properties.add(EnumProperty<TextWidthBasis>('textWidthBasis', textWidthBasis, defaultValue: TextWidthBasis.parent));
203 properties.add(DiagnosticsProperty<ui.TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
204 }
205}
206
207class _NullWidget extends StatelessWidget {
208 const _NullWidget();
209
210 @override
211 Widget build(BuildContext context) {
212 throw FlutterError(
213 'A DefaultTextStyle constructed with DefaultTextStyle.fallback cannot be incorporated into the widget tree, '
214 'it is meant only to provide a fallback value returned by DefaultTextStyle.of() '
215 'when no enclosing default text style is present in a BuildContext.',
216 );
217 }
218}
219
220/// The [TextHeightBehavior] that will apply to descendant [Text] and [EditableText]
221/// widgets which have not explicitly set [Text.textHeightBehavior].
222///
223/// If there is a [DefaultTextStyle] with a non-null [DefaultTextStyle.textHeightBehavior]
224/// below this widget, the [DefaultTextStyle.textHeightBehavior] will be used
225/// over this widget's [TextHeightBehavior].
226///
227/// See also:
228///
229/// * [DefaultTextStyle], which defines a [TextStyle] to apply to descendant
230/// [Text] widgets.
231class DefaultTextHeightBehavior extends InheritedTheme {
232 /// Creates a default text height behavior for the given subtree.
233 const DefaultTextHeightBehavior({
234 super.key,
235 required this.textHeightBehavior,
236 required super.child,
237 });
238
239 /// {@macro dart.ui.textHeightBehavior}
240 final TextHeightBehavior textHeightBehavior;
241
242 /// The closest instance of [DefaultTextHeightBehavior] that encloses the
243 /// given context, or null if none is found.
244 ///
245 /// If no such instance exists, this method will return `null`.
246 ///
247 /// Calling this method will create a dependency on the closest
248 /// [DefaultTextHeightBehavior] in the [context], if there is one.
249 ///
250 /// Typical usage is as follows:
251 ///
252 /// ```dart
253 /// TextHeightBehavior? defaultTextHeightBehavior = DefaultTextHeightBehavior.of(context);
254 /// ```
255 ///
256 /// See also:
257 ///
258 /// * [DefaultTextHeightBehavior.maybeOf], which is similar to this method,
259 /// but asserts if no [DefaultTextHeightBehavior] ancestor is found.
260 static TextHeightBehavior? maybeOf(BuildContext context) {
261 return context.dependOnInheritedWidgetOfExactType<DefaultTextHeightBehavior>()?.textHeightBehavior;
262 }
263
264 /// The closest instance of [DefaultTextHeightBehavior] that encloses the
265 /// given context.
266 ///
267 /// If no such instance exists, this method will assert in debug mode, and
268 /// throw an exception in release mode.
269 ///
270 /// Typical usage is as follows:
271 ///
272 /// ```dart
273 /// TextHeightBehavior defaultTextHeightBehavior = DefaultTextHeightBehavior.of(context);
274 /// ```
275 ///
276 /// Calling this method will create a dependency on the closest
277 /// [DefaultTextHeightBehavior] in the [context].
278 ///
279 /// See also:
280 ///
281 /// * [DefaultTextHeightBehavior.maybeOf], which is similar to this method,
282 /// but returns null if no [DefaultTextHeightBehavior] ancestor is found.
283 static TextHeightBehavior of(BuildContext context) {
284 final TextHeightBehavior? behavior = maybeOf(context);
285 assert(() {
286 if (behavior == null) {
287 throw FlutterError(
288 'DefaultTextHeightBehavior.of() was called with a context that does not contain a '
289 'DefaultTextHeightBehavior widget.\n'
290 'No DefaultTextHeightBehavior widget ancestor could be found starting from the '
291 'context that was passed to DefaultTextHeightBehavior.of(). This can happen '
292 'because you are using a widget that looks for a DefaultTextHeightBehavior '
293 'ancestor, but no such ancestor exists.\n'
294 'The context used was:\n'
295 ' $context',
296 );
297 }
298 return true;
299 }());
300 return behavior!;
301 }
302
303 @override
304 bool updateShouldNotify(DefaultTextHeightBehavior oldWidget) {
305 return textHeightBehavior != oldWidget.textHeightBehavior;
306 }
307
308 @override
309 Widget wrap(BuildContext context, Widget child) {
310 return DefaultTextHeightBehavior(
311 textHeightBehavior: textHeightBehavior,
312 child: child,
313 );
314 }
315
316 @override
317 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
318 super.debugFillProperties(properties);
319 properties.add(DiagnosticsProperty<ui.TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
320 }
321}
322
323/// A run of text with a single style.
324///
325/// The [Text] widget displays a string of text with single style. The string
326/// might break across multiple lines or might all be displayed on the same line
327/// depending on the layout constraints.
328///
329/// The [style] argument is optional. When omitted, the text will use the style
330/// from the closest enclosing [DefaultTextStyle]. If the given style's
331/// [TextStyle.inherit] property is true (the default), the given style will
332/// be merged with the closest enclosing [DefaultTextStyle]. This merging
333/// behavior is useful, for example, to make the text bold while using the
334/// default font family and size.
335///
336/// {@tool snippet}
337///
338/// This example shows how to display text using the [Text] widget with the
339/// [overflow] set to [TextOverflow.ellipsis].
340///
341/// ![If the text overflows, the Text widget displays an ellipsis to trim the overflowing text](https://flutter.github.io/assets-for-api-docs/assets/widgets/text_ellipsis.png)
342///
343/// ```dart
344/// Container(
345/// width: 100,
346/// decoration: BoxDecoration(border: Border.all()),
347/// child: Text(overflow: TextOverflow.ellipsis, 'Hello $_name, how are you?'))
348/// ```
349/// {@end-tool}
350///
351/// {@tool snippet}
352///
353/// Setting [maxLines] to `1` is not equivalent to disabling soft wrapping with
354/// [softWrap]. This is apparent when using [TextOverflow.fade] as the following
355/// examples show.
356///
357/// ![If a second line overflows the Text widget displays a horizontal fade](https://flutter.github.io/assets-for-api-docs/assets/widgets/text_fade_max_lines.png)
358///
359/// ```dart
360/// Text(
361/// overflow: TextOverflow.fade,
362/// maxLines: 1,
363/// 'Hello $_name, how are you?')
364/// ```
365///
366/// Here soft wrapping is enabled and the [Text] widget tries to wrap the words
367/// "how are you?" to a second line. This is prevented by the [maxLines] value
368/// of `1`. The result is that a second line overflows and the fade appears in a
369/// horizontal direction at the bottom.
370///
371/// ![If a single line overflows the Text widget displays a horizontal fade](https://flutter.github.io/assets-for-api-docs/assets/widgets/text_fade_soft_wrap.png)
372///
373/// ```dart
374/// Text(
375/// overflow: TextOverflow.fade,
376/// softWrap: false,
377/// 'Hello $_name, how are you?')
378/// ```
379///
380/// Here soft wrapping is disabled with `softWrap: false` and the [Text] widget
381/// attempts to display its text in a single unbroken line. The result is that
382/// the single line overflows and the fade appears in a vertical direction at
383/// the right.
384///
385/// {@end-tool}
386///
387/// Using the [Text.rich] constructor, the [Text] widget can
388/// display a paragraph with differently styled [TextSpan]s. The sample
389/// that follows displays "Hello beautiful world" with different styles
390/// for each word.
391///
392/// {@tool snippet}
393///
394/// ![The word "Hello" is shown with the default text styles. The word "beautiful" is italicized. The word "world" is bold.](https://flutter.github.io/assets-for-api-docs/assets/widgets/text_rich.png)
395///
396/// ```dart
397/// const Text.rich(
398/// TextSpan(
399/// text: 'Hello', // default text style
400/// children: <TextSpan>[
401/// TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)),
402/// TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)),
403/// ],
404/// ),
405/// )
406/// ```
407/// {@end-tool}
408///
409/// ## Interactivity
410///
411/// To make [Text] react to touch events, wrap it in a [GestureDetector] widget
412/// with a [GestureDetector.onTap] handler.
413///
414/// In a Material Design application, consider using a [TextButton] instead, or
415/// if that isn't appropriate, at least using an [InkWell] instead of
416/// [GestureDetector].
417///
418/// To make sections of the text interactive, use [RichText] and specify a
419/// [TapGestureRecognizer] as the [TextSpan.recognizer] of the relevant part of
420/// the text.
421///
422/// ## Selection
423///
424/// [Text] is not selectable by default. To make a [Text] selectable, one can
425/// wrap a subtree with a [SelectionArea] widget. To exclude a part of a subtree
426/// under [SelectionArea] from selection, once can also wrap that part of the
427/// subtree with [SelectionContainer.disabled].
428///
429/// {@tool dartpad}
430/// This sample demonstrates how to disable selection for a Text under a
431/// SelectionArea.
432///
433/// ** See code in examples/api/lib/material/selection_container/selection_container_disabled.0.dart **
434/// {@end-tool}
435///
436/// See also:
437///
438/// * [RichText], which gives you more control over the text styles.
439/// * [DefaultTextStyle], which sets default styles for [Text] widgets.
440/// * [SelectableRegion], which provides an overview of the selection system.
441class Text extends StatelessWidget {
442 /// Creates a text widget.
443 ///
444 /// If the [style] argument is null, the text will use the style from the
445 /// closest enclosing [DefaultTextStyle].
446 ///
447 /// The [overflow] property's behavior is affected by the [softWrap] argument.
448 /// If the [softWrap] is true or null, the glyph causing overflow, and those
449 /// that follow, will not be rendered. Otherwise, it will be shown with the
450 /// given overflow option.
451 const Text(
452 String this.data, {
453 super.key,
454 this.style,
455 this.strutStyle,
456 this.textAlign,
457 this.textDirection,
458 this.locale,
459 this.softWrap,
460 this.overflow,
461 @Deprecated(
462 'Use textScaler instead. '
463 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
464 'This feature was deprecated after v3.12.0-2.0.pre.',
465 )
466 this.textScaleFactor,
467 this.textScaler,
468 this.maxLines,
469 this.semanticsLabel,
470 this.textWidthBasis,
471 this.textHeightBehavior,
472 this.selectionColor,
473 }) : textSpan = null,
474 assert(
475 textScaler == null || textScaleFactor == null,
476 'textScaleFactor is deprecated and cannot be specified when textScaler is specified.',
477 );
478
479 /// Creates a text widget with a [InlineSpan].
480 ///
481 /// The following subclasses of [InlineSpan] may be used to build rich text:
482 ///
483 /// * [TextSpan]s define text and children [InlineSpan]s.
484 /// * [WidgetSpan]s define embedded inline widgets.
485 ///
486 /// See [RichText] which provides a lower-level way to draw text.
487 const Text.rich(
488 InlineSpan this.textSpan, {
489 super.key,
490 this.style,
491 this.strutStyle,
492 this.textAlign,
493 this.textDirection,
494 this.locale,
495 this.softWrap,
496 this.overflow,
497 @Deprecated(
498 'Use textScaler instead. '
499 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
500 'This feature was deprecated after v3.12.0-2.0.pre.',
501 )
502 this.textScaleFactor,
503 this.textScaler,
504 this.maxLines,
505 this.semanticsLabel,
506 this.textWidthBasis,
507 this.textHeightBehavior,
508 this.selectionColor,
509 }) : data = null,
510 assert(
511 textScaler == null || textScaleFactor == null,
512 'textScaleFactor is deprecated and cannot be specified when textScaler is specified.',
513 );
514
515 /// The text to display.
516 ///
517 /// This will be null if a [textSpan] is provided instead.
518 final String? data;
519
520 /// The text to display as a [InlineSpan].
521 ///
522 /// This will be null if [data] is provided instead.
523 final InlineSpan? textSpan;
524
525 /// If non-null, the style to use for this text.
526 ///
527 /// If the style's "inherit" property is true, the style will be merged with
528 /// the closest enclosing [DefaultTextStyle]. Otherwise, the style will
529 /// replace the closest enclosing [DefaultTextStyle].
530 final TextStyle? style;
531
532 /// {@macro flutter.painting.textPainter.strutStyle}
533 final StrutStyle? strutStyle;
534
535 /// How the text should be aligned horizontally.
536 final TextAlign? textAlign;
537
538 /// The directionality of the text.
539 ///
540 /// This decides how [textAlign] values like [TextAlign.start] and
541 /// [TextAlign.end] are interpreted.
542 ///
543 /// This is also used to disambiguate how to render bidirectional text. For
544 /// example, if the [data] is an English phrase followed by a Hebrew phrase,
545 /// in a [TextDirection.ltr] context the English phrase will be on the left
546 /// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
547 /// context, the English phrase will be on the right and the Hebrew phrase on
548 /// its left.
549 ///
550 /// Defaults to the ambient [Directionality], if any.
551 final TextDirection? textDirection;
552
553 /// Used to select a font when the same Unicode character can
554 /// be rendered differently, depending on the locale.
555 ///
556 /// It's rarely necessary to set this property. By default its value
557 /// is inherited from the enclosing app with `Localizations.localeOf(context)`.
558 ///
559 /// See [RenderParagraph.locale] for more information.
560 final Locale? locale;
561
562 /// Whether the text should break at soft line breaks.
563 ///
564 /// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
565 final bool? softWrap;
566
567 /// How visual overflow should be handled.
568 ///
569 /// If this is null [TextStyle.overflow] will be used, otherwise the value
570 /// from the nearest [DefaultTextStyle] ancestor will be used.
571 final TextOverflow? overflow;
572
573 /// Deprecated. Will be removed in a future version of Flutter. Use
574 /// [textScaler] instead.
575 ///
576 /// The number of font pixels for each logical pixel.
577 ///
578 /// For example, if the text scale factor is 1.5, text will be 50% larger than
579 /// the specified font size.
580 ///
581 /// The value given to the constructor as textScaleFactor. If null, will
582 /// use the [MediaQueryData.textScaleFactor] obtained from the ambient
583 /// [MediaQuery], or 1.0 if there is no [MediaQuery] in scope.
584 @Deprecated(
585 'Use textScaler instead. '
586 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
587 'This feature was deprecated after v3.12.0-2.0.pre.',
588 )
589 final double? textScaleFactor;
590
591 /// {@macro flutter.painting.textPainter.textScaler}
592 final TextScaler? textScaler;
593
594 /// An optional maximum number of lines for the text to span, wrapping if necessary.
595 /// If the text exceeds the given number of lines, it will be truncated according
596 /// to [overflow].
597 ///
598 /// If this is 1, text will not wrap. Otherwise, text will be wrapped at the
599 /// edge of the box.
600 ///
601 /// If this is null, but there is an ambient [DefaultTextStyle] that specifies
602 /// an explicit number for its [DefaultTextStyle.maxLines], then the
603 /// [DefaultTextStyle] value will take precedence. You can use a [RichText]
604 /// widget directly to entirely override the [DefaultTextStyle].
605 final int? maxLines;
606
607 /// {@template flutter.widgets.Text.semanticsLabel}
608 /// An alternative semantics label for this text.
609 ///
610 /// If present, the semantics of this widget will contain this value instead
611 /// of the actual text. This will overwrite any of the semantics labels applied
612 /// directly to the [TextSpan]s.
613 ///
614 /// This is useful for replacing abbreviations or shorthands with the full
615 /// text value:
616 ///
617 /// ```dart
618 /// const Text(r'$$', semanticsLabel: 'Double dollars')
619 /// ```
620 /// {@endtemplate}
621 final String? semanticsLabel;
622
623 /// {@macro flutter.painting.textPainter.textWidthBasis}
624 final TextWidthBasis? textWidthBasis;
625
626 /// {@macro dart.ui.textHeightBehavior}
627 final ui.TextHeightBehavior? textHeightBehavior;
628
629 /// The color to use when painting the selection.
630 ///
631 /// This is ignored if [SelectionContainer.maybeOf] returns null
632 /// in the [BuildContext] of the [Text] widget.
633 ///
634 /// If null, the ambient [DefaultSelectionStyle] is used (if any); failing
635 /// that, the selection color defaults to [DefaultSelectionStyle.defaultColor]
636 /// (semi-transparent grey).
637 final Color? selectionColor;
638
639 @override
640 Widget build(BuildContext context) {
641 final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
642 TextStyle? effectiveTextStyle = style;
643 if (style == null || style!.inherit) {
644 effectiveTextStyle = defaultTextStyle.style.merge(style);
645 }
646 if (MediaQuery.boldTextOf(context)) {
647 effectiveTextStyle = effectiveTextStyle!.merge(const TextStyle(fontWeight: FontWeight.bold));
648 }
649 final SelectionRegistrar? registrar = SelectionContainer.maybeOf(context);
650 final TextScaler textScaler = switch ((this.textScaler, textScaleFactor)) {
651 (final TextScaler textScaler, _) => textScaler,
652 // For unmigrated apps, fall back to textScaleFactor.
653 (null, final double textScaleFactor) => TextScaler.linear(textScaleFactor),
654 (null, null) => MediaQuery.textScalerOf(context),
655 };
656
657 Widget result = RichText(
658 textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
659 textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null.
660 locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null
661 softWrap: softWrap ?? defaultTextStyle.softWrap,
662 overflow: overflow ?? effectiveTextStyle?.overflow ?? defaultTextStyle.overflow,
663 textScaler: textScaler,
664 maxLines: maxLines ?? defaultTextStyle.maxLines,
665 strutStyle: strutStyle,
666 textWidthBasis: textWidthBasis ?? defaultTextStyle.textWidthBasis,
667 textHeightBehavior: textHeightBehavior ?? defaultTextStyle.textHeightBehavior ?? DefaultTextHeightBehavior.maybeOf(context),
668 selectionRegistrar: registrar,
669 selectionColor: selectionColor ?? DefaultSelectionStyle.of(context).selectionColor ?? DefaultSelectionStyle.defaultColor,
670 text: TextSpan(
671 style: effectiveTextStyle,
672 text: data,
673 children: textSpan != null ? <InlineSpan>[textSpan!] : null,
674 ),
675 );
676 if (registrar != null) {
677 result = MouseRegion(
678 cursor: DefaultSelectionStyle.of(context).mouseCursor ?? SystemMouseCursors.text,
679 child: result,
680 );
681 }
682 if (semanticsLabel != null) {
683 result = Semantics(
684 textDirection: textDirection,
685 label: semanticsLabel,
686 child: ExcludeSemantics(
687 child: result,
688 ),
689 );
690 }
691 return result;
692 }
693
694 @override
695 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
696 super.debugFillProperties(properties);
697 properties.add(StringProperty('data', data, showName: false));
698 if (textSpan != null) {
699 properties.add(textSpan!.toDiagnosticsNode(name: 'textSpan', style: DiagnosticsTreeStyle.transition));
700 }
701 style?.debugFillProperties(properties);
702 properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
703 properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
704 properties.add(DiagnosticsProperty<Locale>('locale', locale, defaultValue: null));
705 properties.add(FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
706 properties.add(EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
707 properties.add(DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
708 properties.add(IntProperty('maxLines', maxLines, defaultValue: null));
709 properties.add(EnumProperty<TextWidthBasis>('textWidthBasis', textWidthBasis, defaultValue: null));
710 properties.add(DiagnosticsProperty<ui.TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
711 if (semanticsLabel != null) {
712 properties.add(StringProperty('semanticsLabel', semanticsLabel));
713 }
714 }
715}
716