mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-08-04 01:30:14 +08:00
455 lines
17 KiB
Dart
455 lines
17 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// ignore_for_file: prefer_initializing_formals, uri_does_not_exist_in_doc_import
|
|
|
|
/// @docImport 'package:flutter/gestures.dart';
|
|
/// @docImport 'package:flutter/material.dart';
|
|
///
|
|
/// @docImport 'editable_text.dart';
|
|
/// @docImport 'gesture_detector.dart';
|
|
/// @docImport 'implicit_animations.dart';
|
|
/// @docImport 'transitions.dart';
|
|
/// @docImport 'widget_span.dart';
|
|
library;
|
|
|
|
import 'dart:ui' as ui show TextHeightBehavior;
|
|
|
|
import 'package:PiliPlus/common/widgets/more_text/rich_text_more.dart';
|
|
import 'package:flutter/material.dart' hide Text;
|
|
|
|
/// A run of text with a single style.
|
|
///
|
|
/// The [Text] widget displays a string of text with single style. The string
|
|
/// might break across multiple lines or might all be displayed on the same line
|
|
/// depending on the layout constraints.
|
|
///
|
|
/// The [style] argument is optional. When omitted, the text will use the style
|
|
/// from the closest enclosing [DefaultTextStyle]. If the given style's
|
|
/// [TextStyle.inherit] property is true (the default), the given style will
|
|
/// be merged with the closest enclosing [DefaultTextStyle]. This merging
|
|
/// behavior is useful, for example, to make the text bold while using the
|
|
/// default font family and size.
|
|
///
|
|
/// {@tool snippet}
|
|
///
|
|
/// This example shows how to display text using the [Text] widget with the
|
|
/// [overflow] set to [TextOverflow.ellipsis].
|
|
///
|
|
/// 
|
|
///
|
|
/// ```dart
|
|
/// Container(
|
|
/// width: 100,
|
|
/// decoration: BoxDecoration(border: Border.all()),
|
|
/// child: const Text(
|
|
/// 'Hello, how are you?',
|
|
/// overflow: TextOverflow.ellipsis,
|
|
/// ),
|
|
/// )
|
|
/// ```
|
|
/// {@end-tool}
|
|
///
|
|
/// {@tool snippet}
|
|
///
|
|
/// Setting [maxLines] to `1` is not equivalent to disabling soft wrapping with
|
|
/// [softWrap]. This is apparent when using [TextOverflow.fade] as the following
|
|
/// examples show.
|
|
///
|
|
/// 
|
|
///
|
|
/// ```dart
|
|
/// const Text(
|
|
/// 'Hello, how are you?',
|
|
/// overflow: TextOverflow.fade,
|
|
/// maxLines: 1,
|
|
/// )
|
|
/// ```
|
|
///
|
|
/// Here soft wrapping is enabled and the [Text] widget tries to wrap the words
|
|
/// "how are you?" to a second line. This is prevented by the [maxLines] value
|
|
/// of `1`. The result is that a second line overflows and the fade appears in a
|
|
/// horizontal direction at the bottom.
|
|
///
|
|
/// 
|
|
///
|
|
/// ```dart
|
|
/// const Text(
|
|
/// 'Hello, how are you?',
|
|
/// overflow: TextOverflow.fade,
|
|
/// softWrap: false,
|
|
/// )
|
|
/// ```
|
|
///
|
|
/// Here soft wrapping is disabled with `softWrap: false` and the [Text] widget
|
|
/// attempts to display its text in a single unbroken line. The result is that
|
|
/// the single line overflows and the fade appears in a vertical direction at
|
|
/// the right.
|
|
///
|
|
/// {@end-tool}
|
|
///
|
|
/// Using the [Text.rich] constructor, the [Text] widget can
|
|
/// display a paragraph with differently styled [TextSpan]s. The sample
|
|
/// that follows displays "Hello beautiful world" with different styles
|
|
/// for each word.
|
|
///
|
|
/// {@tool snippet}
|
|
///
|
|
/// 
|
|
///
|
|
/// ```dart
|
|
/// const Text.rich(
|
|
/// TextSpan(
|
|
/// text: 'Hello', // default text style
|
|
/// children: <TextSpan>[
|
|
/// TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)),
|
|
/// TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
/// ],
|
|
/// ),
|
|
/// )
|
|
/// ```
|
|
/// {@end-tool}
|
|
///
|
|
/// ## Interactivity
|
|
///
|
|
/// To make [Text] react to touch events, wrap it in a [GestureDetector] widget
|
|
/// with a [GestureDetector.onTap] handler.
|
|
///
|
|
/// In a Material Design application, consider using a [TextButton] instead, or
|
|
/// if that isn't appropriate, at least using an [InkWell] instead of
|
|
/// [GestureDetector].
|
|
///
|
|
/// To make sections of the text interactive, use [RichText] and specify a
|
|
/// [TapGestureRecognizer] as the [TextSpan.recognizer] of the relevant part of
|
|
/// the text.
|
|
///
|
|
/// ## Selection
|
|
///
|
|
/// [Text] is not selectable by default. To make a [Text] selectable, one can
|
|
/// wrap a subtree with a [SelectionArea] widget. To exclude a part of a subtree
|
|
/// under [SelectionArea] from selection, once can also wrap that part of the
|
|
/// subtree with [SelectionContainer.disabled].
|
|
///
|
|
/// {@tool dartpad}
|
|
/// This sample demonstrates how to disable selection for a Text under a
|
|
/// SelectionArea.
|
|
///
|
|
/// ** See code in examples/api/lib/material/selection_container/selection_container_disabled.0.dart **
|
|
/// {@end-tool}
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [RichText], which gives you more control over the text styles.
|
|
/// * [DefaultTextStyle], which sets default styles for [Text] widgets.
|
|
/// * [SelectableRegion], which provides an overview of the selection system.
|
|
class Text extends StatelessWidget {
|
|
/// Creates a text widget.
|
|
///
|
|
/// If the [style] argument is null, the text will use the style from the
|
|
/// closest enclosing [DefaultTextStyle].
|
|
///
|
|
/// The [overflow] property's behavior is affected by the [softWrap] argument.
|
|
/// If the [softWrap] is true or null, the glyph causing overflow, and those
|
|
/// that follow, will not be rendered. Otherwise, it will be shown with the
|
|
/// given overflow option.
|
|
const Text(
|
|
String this.data, {
|
|
super.key,
|
|
this.style,
|
|
this.strutStyle,
|
|
this.textAlign,
|
|
this.textDirection,
|
|
this.locale,
|
|
this.softWrap,
|
|
this.overflow,
|
|
@Deprecated(
|
|
'Use textScaler instead. '
|
|
'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
|
|
'This feature was deprecated after v3.12.0-2.0.pre.',
|
|
)
|
|
this.textScaleFactor,
|
|
this.textScaler,
|
|
this.maxLines,
|
|
this.semanticsLabel,
|
|
this.semanticsIdentifier,
|
|
this.textWidthBasis,
|
|
this.textHeightBehavior,
|
|
this.selectionColor,
|
|
required this.primary,
|
|
this.onShowMore,
|
|
}) : textSpan = null,
|
|
assert(
|
|
textScaler == null || textScaleFactor == null,
|
|
'textScaleFactor is deprecated and cannot be specified when textScaler is specified.',
|
|
);
|
|
|
|
/// Creates a text widget with a [InlineSpan].
|
|
///
|
|
/// The following subclasses of [InlineSpan] may be used to build rich text:
|
|
///
|
|
/// * [TextSpan]s define text and children [InlineSpan]s.
|
|
/// * [WidgetSpan]s define embedded inline widgets.
|
|
///
|
|
/// See [RichText] which provides a lower-level way to draw text.
|
|
const Text.rich(
|
|
InlineSpan this.textSpan, {
|
|
super.key,
|
|
this.style,
|
|
this.strutStyle,
|
|
this.textAlign,
|
|
this.textDirection,
|
|
this.locale,
|
|
this.softWrap,
|
|
this.overflow,
|
|
@Deprecated(
|
|
'Use textScaler instead. '
|
|
'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
|
|
'This feature was deprecated after v3.12.0-2.0.pre.',
|
|
)
|
|
this.textScaleFactor,
|
|
this.textScaler,
|
|
this.maxLines,
|
|
this.semanticsLabel,
|
|
this.semanticsIdentifier,
|
|
this.textWidthBasis,
|
|
this.textHeightBehavior,
|
|
this.selectionColor,
|
|
required this.primary,
|
|
this.onShowMore,
|
|
}) : data = null,
|
|
assert(
|
|
textScaler == null || textScaleFactor == null,
|
|
'textScaleFactor is deprecated and cannot be specified when textScaler is specified.',
|
|
);
|
|
|
|
/// The text to display.
|
|
///
|
|
/// This will be null if a [textSpan] is provided instead.
|
|
final String? data;
|
|
|
|
/// The text to display as a [InlineSpan].
|
|
///
|
|
/// This will be null if [data] is provided instead.
|
|
final InlineSpan? textSpan;
|
|
|
|
/// If non-null, the style to use for this text.
|
|
///
|
|
/// If the style's "inherit" property is true, the style will be merged with
|
|
/// the closest enclosing [DefaultTextStyle]. Otherwise, the style will
|
|
/// replace the closest enclosing [DefaultTextStyle].
|
|
///
|
|
/// The user or platform may override this [style]'s [TextStyle.fontWeight],
|
|
/// [TextStyle.height], [TextStyle.letterSpacing], and [TextStyle.wordSpacing]
|
|
/// via a [MediaQuery] ancestor's [MediaQueryData.boldText],
|
|
/// [MediaQueryData.lineHeightScaleFactorOverride],
|
|
/// [MediaQueryData.letterSpacingOverride], and [MediaQueryData.wordSpacingOverride]
|
|
/// regardless of its [TextStyle.inherit] value.
|
|
final TextStyle? style;
|
|
|
|
/// {@macro flutter.painting.textPainter.strutStyle}
|
|
///
|
|
/// The user or platform may override this [strutStyle]'s [StrutStyle.height]
|
|
/// via a [MediaQuery] ancestor's [MediaQueryData.lineHeightScaleFactorOverride].
|
|
final StrutStyle? strutStyle;
|
|
|
|
/// How the text should be aligned horizontally.
|
|
final TextAlign? textAlign;
|
|
|
|
/// The directionality of the text.
|
|
///
|
|
/// This decides how [textAlign] values like [TextAlign.start] and
|
|
/// [TextAlign.end] are interpreted.
|
|
///
|
|
/// This is also used to disambiguate how to render bidirectional text. For
|
|
/// example, if the [data] is an English phrase followed by a Hebrew phrase,
|
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
|
/// context, the English phrase will be on the right and the Hebrew phrase on
|
|
/// its left.
|
|
///
|
|
/// Defaults to the ambient [Directionality], if any.
|
|
final TextDirection? textDirection;
|
|
|
|
/// Used to select a font when the same Unicode character can
|
|
/// be rendered differently, depending on the locale.
|
|
///
|
|
/// It's rarely necessary to set this property. By default its value
|
|
/// is inherited from the enclosing app with `Localizations.localeOf(context)`.
|
|
///
|
|
/// See [RenderParagraph.locale] for more information.
|
|
final Locale? locale;
|
|
|
|
/// Whether the text should break at soft line breaks.
|
|
///
|
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
|
final bool? softWrap;
|
|
|
|
/// How visual overflow should be handled.
|
|
///
|
|
/// If this is null [TextStyle.overflow] will be used, otherwise the value
|
|
/// from the nearest [DefaultTextStyle] ancestor will be used.
|
|
final TextOverflow? overflow;
|
|
|
|
/// Deprecated. Will be removed in a future version of Flutter. Use
|
|
/// [textScaler] instead.
|
|
///
|
|
/// The number of font pixels for each logical pixel.
|
|
///
|
|
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
|
/// the specified font size.
|
|
///
|
|
/// The value given to the constructor as textScaleFactor. If null, will
|
|
/// use the [MediaQueryData.textScaleFactor] obtained from the ambient
|
|
/// [MediaQuery], or 1.0 if there is no [MediaQuery] in scope.
|
|
@Deprecated(
|
|
'Use textScaler instead. '
|
|
'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
|
|
'This feature was deprecated after v3.12.0-2.0.pre.',
|
|
)
|
|
final double? textScaleFactor;
|
|
|
|
/// {@macro flutter.painting.textPainter.textScaler}
|
|
final TextScaler? textScaler;
|
|
|
|
/// An optional maximum number of lines for the text to span, wrapping if necessary.
|
|
/// If the text exceeds the given number of lines, it will be truncated according
|
|
/// to [overflow].
|
|
///
|
|
/// If this is 1, text will not wrap. Otherwise, text will be wrapped at the
|
|
/// edge of the box.
|
|
///
|
|
/// If this is null, but there is an ambient [DefaultTextStyle] that specifies
|
|
/// an explicit number for its [DefaultTextStyle.maxLines], then the
|
|
/// [DefaultTextStyle] value will take precedence. You can use a [RichText]
|
|
/// widget directly to entirely override the [DefaultTextStyle].
|
|
final int? maxLines;
|
|
|
|
/// {@template flutter.widgets.Text.semanticsLabel}
|
|
/// An alternative semantics label for this text.
|
|
///
|
|
/// If present, the semantics of this widget will contain this value instead
|
|
/// of the actual text. This will overwrite any of the semantics labels applied
|
|
/// directly to the [TextSpan]s.
|
|
///
|
|
/// This is useful for replacing abbreviations or shorthands with the full
|
|
/// text value:
|
|
///
|
|
/// ```dart
|
|
/// const Text(r'$$', semanticsLabel: 'Double dollars')
|
|
/// ```
|
|
/// {@endtemplate}
|
|
final String? semanticsLabel;
|
|
|
|
/// A unique identifier for the semantics node for this widget.
|
|
///
|
|
/// This is useful for cases where the text widget needs to have a uniquely
|
|
/// identifiable ID that is recognized through the automation tools without
|
|
/// having a dependency on the actual content of the text that can possibly be
|
|
/// dynamic in nature.
|
|
final String? semanticsIdentifier;
|
|
|
|
/// {@macro flutter.painting.textPainter.textWidthBasis}
|
|
final TextWidthBasis? textWidthBasis;
|
|
|
|
/// {@macro dart.ui.textHeightBehavior}
|
|
final ui.TextHeightBehavior? textHeightBehavior;
|
|
|
|
/// The color to use when painting the selection.
|
|
///
|
|
/// This is ignored if [SelectionContainer.maybeOf] returns null
|
|
/// in the [BuildContext] of the [Text] widget.
|
|
///
|
|
/// If null, the ambient [DefaultSelectionStyle] is used (if any); failing
|
|
/// that, the selection color defaults to [DefaultSelectionStyle.defaultColor]
|
|
/// (semi-transparent grey).
|
|
final Color? selectionColor;
|
|
|
|
final Color primary;
|
|
|
|
final VoidCallback? onShowMore;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
|
|
TextStyle? effectiveTextStyle = style;
|
|
if (style == null || style!.inherit) {
|
|
effectiveTextStyle = defaultTextStyle.style.merge(style);
|
|
}
|
|
if (MediaQuery.boldTextOf(context)) {
|
|
effectiveTextStyle = effectiveTextStyle!.merge(
|
|
const TextStyle(fontWeight: FontWeight.bold),
|
|
);
|
|
}
|
|
// TODO(Renzo-Olivares): Investigate ways the framework can automatically
|
|
// apply MediaQueryData.paragraphSpacingOverride to its own text components.
|
|
// See: https://github.com/flutter/flutter/issues/177953 and https://github.com/flutter/flutter/issues/177408.
|
|
final double? lineHeightScaleFactor =
|
|
MediaQuery.maybeLineHeightScaleFactorOverrideOf(context);
|
|
final double? letterSpacing = MediaQuery.maybeLetterSpacingOverrideOf(
|
|
context,
|
|
);
|
|
final double? wordSpacing = MediaQuery.maybeWordSpacingOverrideOf(context);
|
|
final TextSpan effectiveTextSpan =
|
|
OverridingTextStyleTextSpanUtils.applyTextSpacingOverrides(
|
|
lineHeightScaleFactor: lineHeightScaleFactor,
|
|
letterSpacing: letterSpacing,
|
|
wordSpacing: wordSpacing,
|
|
textSpan: TextSpan(
|
|
style: effectiveTextStyle,
|
|
text: data,
|
|
locale: locale,
|
|
children: textSpan != null ? <InlineSpan>[textSpan!] : null,
|
|
),
|
|
);
|
|
final StrutStyle? effectiveStrutStyle = strutStyle?.merge(
|
|
StrutStyle(height: lineHeightScaleFactor),
|
|
);
|
|
final TextScaler textScaler = switch ((this.textScaler, textScaleFactor)) {
|
|
(final TextScaler textScaler, _) => textScaler,
|
|
// For unmigrated apps, fall back to textScaleFactor.
|
|
(null, final double textScaleFactor) => TextScaler.linear(
|
|
textScaleFactor,
|
|
),
|
|
(null, null) => MediaQuery.textScalerOf(context),
|
|
};
|
|
Widget result = RichTextMore(
|
|
textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
|
|
textDirection:
|
|
textDirection, // RichText uses Directionality.of to obtain a default if this is null.
|
|
locale:
|
|
locale, // RichText uses Localizations.localeOf to obtain a default if this is null
|
|
softWrap: softWrap ?? defaultTextStyle.softWrap,
|
|
overflow:
|
|
overflow ?? effectiveTextStyle?.overflow ?? defaultTextStyle.overflow,
|
|
textScaler: textScaler,
|
|
maxLines: maxLines ?? defaultTextStyle.maxLines,
|
|
strutStyle: effectiveStrutStyle,
|
|
textWidthBasis: textWidthBasis ?? defaultTextStyle.textWidthBasis,
|
|
textHeightBehavior:
|
|
textHeightBehavior ??
|
|
defaultTextStyle.textHeightBehavior ??
|
|
DefaultTextHeightBehavior.maybeOf(context),
|
|
selectionColor:
|
|
selectionColor ??
|
|
DefaultSelectionStyle.of(context).selectionColor ??
|
|
DefaultSelectionStyle.defaultColor,
|
|
text: effectiveTextSpan,
|
|
primary: primary,
|
|
onShowMore: onShowMore,
|
|
);
|
|
if (semanticsLabel != null || semanticsIdentifier != null) {
|
|
result = Semantics(
|
|
textDirection: textDirection,
|
|
label: semanticsLabel,
|
|
identifier: semanticsIdentifier,
|
|
child: ExcludeSemantics(
|
|
excluding: semanticsLabel != null,
|
|
child: result,
|
|
),
|
|
);
|
|
}
|
|
return result;
|
|
}
|
|
}
|