Revert "Reland "Text inline widgets, TextSpan rework" (#33946)" (#34002)
This reverts commit 14414f350ac040f9626f1aa0b3071f42cc9136a6.
diff --git a/dev/benchmarks/complex_layout/lib/main.dart b/dev/benchmarks/complex_layout/lib/main.dart
index 634be45..2d349ae 100644
--- a/dev/benchmarks/complex_layout/lib/main.dart
+++ b/dev/benchmarks/complex_layout/lib/main.dart
@@ -438,8 +438,8 @@
borderRadius: BorderRadius.circular(2.0),
),
padding: const EdgeInsets.all(4.0),
- child: RichText(
- text: const TextSpan(
+ child: const RichText(
+ text: TextSpan(
style: TextStyle(color: Colors.white),
children: <TextSpan>[
TextSpan(
diff --git a/dev/manual_tests/lib/text.dart b/dev/manual_tests/lib/text.dart
index 3d5f482..6382fb3 100644
--- a/dev/manual_tests/lib/text.dart
+++ b/dev/manual_tests/lib/text.dart
@@ -145,7 +145,7 @@
return TextSpan(
text: _fiddleWithText(node.text),
style: _fiddleWithStyle(node.style),
- children: _fiddleWithChildren(node.children?.map((InlineSpan child) => _fiddleWith(child))?.toList() ?? <InlineSpan>[]),
+ children: _fiddleWithChildren(node.children?.map((TextSpan child) => _fiddleWith(child))?.toList() ?? <TextSpan>[]),
);
}
diff --git a/packages/flutter/lib/painting.dart b/packages/flutter/lib/painting.dart
index a22d5bf..fe432cb 100644
--- a/packages/flutter/lib/painting.dart
+++ b/packages/flutter/lib/painting.dart
@@ -17,7 +17,7 @@
/// painting boxes.
library painting;
-export 'dart:ui' show Shadow, PlaceholderAlignment;
+export 'dart:ui' show Shadow;
export 'src/painting/alignment.dart';
export 'src/painting/basic_types.dart';
@@ -46,11 +46,9 @@
export 'src/painting/image_provider.dart';
export 'src/painting/image_resolution.dart';
export 'src/painting/image_stream.dart';
-export 'src/painting/inline_span.dart';
export 'src/painting/matrix_utils.dart';
export 'src/painting/notched_shapes.dart';
export 'src/painting/paint_utilities.dart';
-export 'src/painting/placeholder_span.dart';
export 'src/painting/rounded_rectangle_border.dart';
export 'src/painting/shader_warm_up.dart';
export 'src/painting/shape_decoration.dart';
diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart
index 8eb322b..9d720aa 100644
--- a/packages/flutter/lib/src/material/time_picker.dart
+++ b/packages/flutter/lib/src/material/time_picker.dart
@@ -993,7 +993,6 @@
final double width = labelPainter.width * _semanticNodeSizeScale;
final double height = labelPainter.height * _semanticNodeSizeScale;
final Offset nodeOffset = getOffsetForTheta(labelTheta, ring) + Offset(-width / 2.0, -height / 2.0);
- final TextSpan textSpan = labelPainter.text;
final CustomPainterSemantics node = CustomPainterSemantics(
rect: Rect.fromLTRB(
nodeOffset.dx - 24.0 + width / 2,
@@ -1004,7 +1003,7 @@
properties: SemanticsProperties(
sortKey: OrdinalSortKey(i.toDouble() + ordinalOffset),
selected: label.value == selectedValue,
- value: textSpan?.text,
+ value: labelPainter.text.text,
textDirection: textDirection,
onTap: label.onTap,
),
diff --git a/packages/flutter/lib/src/painting/inline_span.dart b/packages/flutter/lib/src/painting/inline_span.dart
deleted file mode 100644
index ac09ded..0000000
--- a/packages/flutter/lib/src/painting/inline_span.dart
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:ui' as ui show ParagraphBuilder;
-
-import 'package:flutter/foundation.dart';
-
-import 'basic_types.dart';
-import 'text_painter.dart';
-import 'text_style.dart';
-
-/// Mutable wrapper of an integer that can be passed by reference to track a
-/// value across a recursive stack.
-class Accumulator {
- /// [Accumulator] may be initialized with a specified value, otherwise, it will
- /// initialize to zero.
- Accumulator([this._value = 0]);
-
- /// The integer stored in this [Accumulator].
- int get value => _value;
- int _value;
-
- /// Increases the [value] by the `addend`.
- void increment(int addend) {
- assert(addend >= 0);
- _value += addend;
- }
-}
-/// Called on each span as [InlineSpan.visitChildren] walks the [InlineSpan] tree.
-///
-/// Returns true when the walk should continue, and false to stop visiting further
-/// [InlineSpan]s.
-typedef InlineSpanVisitor = bool Function(InlineSpan span);
-
-/// An immutable span of inline content which forms part of a paragraph.
-///
-/// * The subclass [TextSpan] specifies text and may contain child [InlineSpan]s.
-/// * The subclass [PlaceholderSpan] represents a placeholder that may be
-/// filled with non-text content. [PlaceholderSpan] itself defines a
-/// [ui.PlaceholderAlignemnt] and a [TextBaseline]. To be useful,
-/// [PlaceholderSpan] must be extended to define content. An instance of
-/// this is the [WidgetSpan] class in the widgets library.
-/// * The subclass [WidgetSpan] specifies embedded inline widgets.
-///
-/// {@tool sample}
-///
-/// This example shows a tree of [InlineSpan]s that make a query asking for a
-/// name with a [TextField] embedded inline.
-///
-/// ```dart
-/// Text.rich(
-/// TextSpan(
-/// text: 'My name is ',
-/// style: TextStyle(color: Colors.black),
-/// children: <InlineSpan>[
-/// WidgetSpan(
-/// alignment: PlaceholderAlignment.baseline,
-/// baseline: TextBaseline.alphabetic,
-/// child: ConstrainedBox(
-/// constraints: BoxConstraints(maxWidth: 100),
-/// child: TextField(),
-/// )
-/// ),
-/// TextSpan(
-/// text: '.',
-/// ),
-/// ],
-/// ),
-/// )
-/// ```
-/// {@end-tool}
-///
-/// See also:
-///
-/// * [Text], a widget for showing uniformly-styled text.
-/// * [RichText], a widget for finer control of text rendering.
-/// * [TextPainter], a class for painting [InlineSpan] objects on a [Canvas].
-@immutable
-abstract class InlineSpan extends DiagnosticableTree {
- /// Creates an [InlineSpan] with the given values.
- const InlineSpan({
- this.style,
- });
-
- /// The [TextStyle] to apply to this span.
- ///
- /// The [style] is also applied to any child spans when this is an instance
- /// of [TextSpan].
- final TextStyle style;
-
- /// Apply the properties of this object to the given [ParagraphBuilder], from
- /// which a [Paragraph] can be obtained.
- ///
- /// The `textScaleFactor` parameter specifies a scale that the text and
- /// placeholders will be scaled by. The scaling is performed before layout,
- /// so the text will be laid out with the scaled glyphs and placeholders.
- ///
- /// The `dimensions` parameter specifies the sizes of the placeholders.
- /// Each [PlaceholderSpan] must be paired with a [PlaceholderDimensions]
- /// in the same order as defined in the [InlineSpan] tree.
- ///
- /// [Paragraph] objects can be drawn on [Canvas] objects.
- void build(ui.ParagraphBuilder builder, { double textScaleFactor = 1.0, List<PlaceholderDimensions> dimensions });
-
- /// Walks this [InlineSpan] and any descendants in pre-order and calls `visitor`
- /// for each span that has content.
- ///
- /// When `visitor` returns true, the walk will continue. When `visitor` returns
- /// false, then the walk will end.
- bool visitChildren(InlineSpanVisitor visitor);
-
- /// Returns the text span that contains the given position in the text.
- InlineSpan getSpanForPosition(TextPosition position) {
- assert(debugAssertIsValid());
- final Accumulator offset = Accumulator();
- InlineSpan result;
- visitChildren((InlineSpan span) {
- result = span.getSpanForPositionVisitor(position, offset);
- return result == null;
- });
- return result;
- }
-
- /// Performs the check at each [InlineSpan] for if the `position` falls within the range
- /// of the span and returns the span if it does.
- ///
- /// The `offset` parameter tracks the current index offset in the text buffer formed
- /// if the contents of the [InlineSpan] tree were concatenated together starting
- /// from the root [InlineSpan].
- ///
- /// This method should not be directly called. Use [getSpanForPosition] instead.
- @protected
- InlineSpan getSpanForPositionVisitor(TextPosition position, Accumulator offset);
-
- /// Flattens the [InlineSpan] tree into a single string.
- ///
- /// Styles are not honored in this process. If `includeSemanticsLabels` is
- /// true, then the text returned will include the [TextSpan.semanticsLabel]s
- /// instead of the text contents for [TextSpan]s.
- ///
- /// When `includePlaceholders` is true, [PlaceholderSpan]s in the tree will be
- /// represented as a 0xFFFC 'object replacement character'.
- String toPlainText({bool includeSemanticsLabels = true, bool includePlaceholders = true}) {
- final StringBuffer buffer = StringBuffer();
- computeToPlainText(buffer, includeSemanticsLabels: includeSemanticsLabels, includePlaceholders: includePlaceholders);
- return buffer.toString();
- }
-
- /// Walks the [InlineSpan] tree and writes the plain text representation to `buffer`.
- ///
- /// This method should not be directly called. Use [toPlainText] instead.
- ///
- /// Styles are not honored in this process. If `includeSemanticsLabels` is
- /// true, then the text returned will include the [TextSpan.semanticsLabel]s
- /// instead of the text contents for [TextSpan]s.
- ///
- /// When `includePlaceholders` is true, [PlaceholderSpan]s in the tree will be
- /// represented as a 0xFFFC 'object replacement character'.
- ///
- /// The plain-text representation of this [InlineSpan] is written into the `buffer`.
- /// This method will then recursively call [computeToPlainText] on its childen
- /// [InlineSpan]s if available.
- @protected
- void computeToPlainText(StringBuffer buffer, {bool includeSemanticsLabels = true, bool includePlaceholders = true});
-
- /// Returns the UTF-16 code unit at the given `index` in the flattened string.
- ///
- /// This only accounts for the [TextSpan.text] values and ignores [PlaceholderSpans].
- ///
- /// Returns null if the `index` is out of bounds.
- int codeUnitAt(int index) {
- if (index < 0)
- return null;
- final Accumulator offset = Accumulator();
- int result;
- visitChildren((InlineSpan span) {
- result = span.codeUnitAtVisitor(index, offset);
- return result == null;
- });
- return result;
- }
-
- /// Performs the check at each [InlineSpan] for if the `index` falls within the range
- /// of the span and returns the corresponding code unit. Returns null otherwise.
- ///
- /// The `offset` parameter tracks the current index offset in the text buffer formed
- /// if the contents of the [InlineSpan] tree were concatenated together starting
- /// from the root [InlineSpan].
- ///
- /// This method should not be directly called. Use [codeUnitAt] instead.
- @protected
- int codeUnitAtVisitor(int index, Accumulator offset);
-
- /// Populates the `semanticsOffsets` and `semanticsElements` with the appropriate data
- /// to be able to construct a [SemanticsNode].
- ///
- /// If applicable, the beginning and end text offset are added to [semanticsOffsets].
- /// [PlaceholderSpan]s have a text length of 1, which corresponds to the object
- /// replacement character (0xFFFC) that is inserted to represent it.
- ///
- /// Any [GestureRecognizer]s are added to `semanticsElements`. Null is added to
- /// `semanticsElements` for [PlaceholderSpan]s.
- void describeSemantics(Accumulator offset, List<int> semanticsOffsets, List<dynamic> semanticsElements);
-
- /// In checked mode, throws an exception if the object is not in a
- /// valid configuration. Otherwise, returns true.
- ///
- /// This is intended to be used as follows:
- ///
- /// ```dart
- /// assert(myInlineSpan.debugAssertIsValid());
- /// ```
- bool debugAssertIsValid() => true;
-
- /// Describe the difference between this span and another, in terms of
- /// how much damage it will make to the rendering. The comparison is deep.
- ///
- /// Comparing [InlineSpan] objects of different types, for example, comparing
- /// a [TextSpan] to a [WidgetSpan], always results in [RenderComparison.layout].
- ///
- /// See also:
- ///
- /// * [TextStyle.compareTo], which does the same thing for [TextStyle]s.
- RenderComparison compareTo(InlineSpan other);
-
- @override
- bool operator ==(dynamic other) {
- if (identical(this, other))
- return true;
- if (other.runtimeType != runtimeType)
- return false;
- final InlineSpan typedOther = other;
- return typedOther.style == style;
- }
-
- @override
- int get hashCode => style.hashCode;
-
- @override
- void debugFillProperties(DiagnosticPropertiesBuilder properties) {
- super.debugFillProperties(properties);
- properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.whitespace;
-
- if (style != null) {
- style.debugFillProperties(properties);
- }
- }
-}
diff --git a/packages/flutter/lib/src/painting/placeholder_span.dart b/packages/flutter/lib/src/painting/placeholder_span.dart
deleted file mode 100644
index e052ea6..0000000
--- a/packages/flutter/lib/src/painting/placeholder_span.dart
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:ui' as ui show PlaceholderAlignment;
-
-import 'package:flutter/foundation.dart';
-
-import 'basic_types.dart';
-import 'inline_span.dart';
-import 'text_painter.dart';
-import 'text_span.dart';
-import 'text_style.dart';
-
-/// An immutable placeholder that is embedded inline within text.
-///
-/// [PlaceholderSpan] represents a placeholder that acts as a stand-in for other
-/// content. A [PlaceholderSpan] by itself does not contain useful
-/// information to change a [TextSpan]. Instead, this class must be extended
-/// to define contents.
-///
-/// [WidgetSpan] from the widgets library extends [PlaceholderSpan] and may be
-/// used instead to specify a widget as the contents of the placeholder.
-///
-/// See also:
-///
-/// * [WidgetSpan], a leaf node that represents an embedded inline widget.
-/// * [TextSpan], a node that represents text in a [TextSpan] tree.
-/// * [Text], a widget for showing uniformly-styled text.
-/// * [RichText], a widget for finer control of text rendering.
-/// * [TextPainter], a class for painting [TextSpan] objects on a [Canvas].
-abstract class PlaceholderSpan extends InlineSpan {
- /// Creates a [PlaceholderSpan] with the given values.
- ///
- /// A [TextStyle] may be provided with the [style] property, but only the
- /// decoration, foreground, background, and spacing options will be used.
- const PlaceholderSpan({
- this.alignment = ui.PlaceholderAlignment.bottom,
- this.baseline,
- TextStyle style,
- }) : super(style: style,);
-
- /// How the placeholder aligns vertically with the text.
- ///
- /// See [ui.PlaceholderAlignment] for details on each mode.
- final ui.PlaceholderAlignment alignment;
-
- /// The [TextBaseline] to align against when using [ui.PlaceholderAlignment.baseline],
- /// [ui.PlaceholderAlignment.aboveBaseline], and [ui.PlaceholderAlignment.belowBaseline].
- ///
- /// This is ignored when using other alignment modes.
- final TextBaseline baseline;
-
- /// [PlaceholderSpan]s are flattened to a `0xFFFC` object replacement character in the
- /// plain text representation when `includePlaceholders` is true.
- @override
- void computeToPlainText(StringBuffer buffer, {bool includeSemanticsLabels = true, bool includePlaceholders = true}) {
- if (includePlaceholders) {
- buffer.write('\uFFFC');
- }
- }
-
- /// Populates the `semanticsOffsets` and `semanticsElements` with the appropriate data
- /// to be able to construct a [SemanticsNode].
- ///
- /// [PlaceholderSpan]s have a text length of 1, which corresponds to the object
- /// replacement character (0xFFFC) that is inserted to represent it.
- ///
- /// Null is added to `semanticsElements` for [PlaceholderSpan]s.
- @override
- void describeSemantics(Accumulator offset, List<int> semanticsOffsets, List<dynamic> semanticsElements) {
- semanticsOffsets.add(offset.value);
- semanticsOffsets.add(offset.value + 1);
- semanticsElements.add(null); // null indicates this is a placeholder.
- offset.increment(1);
- }
-
- @override
- void debugFillProperties(DiagnosticPropertiesBuilder properties) {
- super.debugFillProperties(properties);
-
- properties.add(EnumProperty<ui.PlaceholderAlignment>('alignment', alignment, defaultValue: null));
- properties.add(EnumProperty<TextBaseline>('baseline', baseline, defaultValue: null));
- }
-}
diff --git a/packages/flutter/lib/src/painting/text_painter.dart b/packages/flutter/lib/src/painting/text_painter.dart
index 0c61959..b4f20cb 100644
--- a/packages/flutter/lib/src/painting/text_painter.dart
+++ b/packages/flutter/lib/src/painting/text_painter.dart
@@ -3,82 +3,18 @@
// found in the LICENSE file.
import 'dart:math' show min, max;
-import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle, PlaceholderAlignment;
+import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'basic_types.dart';
-import 'inline_span.dart';
-import 'placeholder_span.dart';
import 'strut_style.dart';
import 'text_span.dart';
export 'package:flutter/services.dart' show TextRange, TextSelection;
-/// Holds the [Size] and baseline required to represent the dimensions of
-/// a placeholder in text.
-///
-/// Placeholders specify an empty space in the text layout, which is used
-/// to later render arbitrary inline widgets into defined by a [WidgetSpan].
-///
-/// The [size] and [alignment] properties are required and cannot be null.
-///
-/// See also:
-///
-/// * [WidgetSpan], a subclass of [InlineSpan] and [PlaceholderSpan] that
-/// represents an inline widget embedded within text. The space this
-/// widget takes is indicated by a placeholder.
-/// * [RichText], a text widget that supports text inline widgets.
-@immutable
-class PlaceholderDimensions {
- /// Constructs a [PlaceholderDimensions] with the specified parameters.
- ///
- /// The `size` and `alignment` are required as a placeholder's dimensions
- /// require at least `size` and `alignment` to be fully defined.
- const PlaceholderDimensions({
- @required this.size,
- @required this.alignment,
- this.baseline,
- this.baselineOffset,
- }) : assert(size != null),
- assert(alignment != null);
-
- /// Width and height dimensions of the placeholder.
- final Size size;
-
- /// How to align the placeholder with the text.
- ///
- /// See also:
- ///
- /// * [baseline], the baseline to align to when using
- /// [ui.PlaceholderAlignment.baseline],
- /// [ui.PlaceholderAlignment.aboveBaseline],
- /// or [ui.PlaceholderAlignment.underBaseline].
- /// * [baselineOffset], the distance of the alphabetic baseline from the upper
- /// edge of the placeholder.
- final ui.PlaceholderAlignment alignment;
-
- /// Distance of the [baseline] from the upper edge of the placeholder.
- ///
- /// Only used when [alignment] is [ui.PlaceholderAlignment.baseline].
- final double baselineOffset;
-
- /// The [TextBaseline] to align to. Used with:
- ///
- /// * [ui.PlaceholderAlignment.baseline]
- /// * [ui.PlaceholderAlignment.aboveBaseline]
- /// * [ui.PlaceholderAlignment.underBaseline]
- /// * [ui.PlaceholderAlignment.middle]
- final TextBaseline baseline;
-
- @override
- String toString() {
- return 'PlaceholderDimensions($size, $baseline)';
- }
-}
-
/// The different ways of considering the width of one or more lines of text.
///
/// See [Text.widthType].
@@ -94,9 +30,6 @@
longestLine,
}
-/// This is used to cache and pass the computed metrics regarding the
-/// caret's size and position. This is preferred due to the expensive
-/// nature of the calculation.
class _CaretMetrics {
const _CaretMetrics({this.offset, this.fullHeight});
/// The offset of the top left corner of the caret from the top left
@@ -134,7 +67,7 @@
///
/// The [maxLines] property, if non-null, must be greater than zero.
TextPainter({
- InlineSpan text,
+ TextSpan text,
TextAlign textAlign = TextAlign.start,
TextDirection textDirection,
double textScaleFactor = 1.0,
@@ -166,9 +99,9 @@
/// After this is set, you must call [layout] before the next call to [paint].
///
/// This and [textDirection] must be non-null before you call [layout].
- InlineSpan get text => _text;
- InlineSpan _text;
- set text(InlineSpan value) {
+ TextSpan get text => _text;
+ TextSpan _text;
+ set text(TextSpan value) {
assert(value == null || value.debugAssertIsValid());
if (_text == value)
return;
@@ -333,53 +266,6 @@
ui.Paragraph _layoutTemplate;
- /// An ordered list of [TextBox]es that bound the positions of the placeholders
- /// in the paragraph.
- ///
- /// Each box corresponds to a [PlaceholderSpan] in the order they were defined
- /// in the [InlineSpan] tree.
- List<TextBox> get inlinePlaceholderBoxes => _inlinePlaceholderBoxes;
- List<TextBox> _inlinePlaceholderBoxes;
-
- /// An ordered list of scales for each placeholder in the paragraph.
- ///
- /// The scale is used as a multiplier on the height, width and baselineOffset of
- /// the placeholder. Scale is primarily used to handle accessibility scaling.
- ///
- /// Each scale corresponds to a [PlaceholderSpan] in the order they were defined
- /// in the [InlineSpan] tree.
- List<double> get inlinePlaceholderScales => _inlinePlaceholderScales;
- List<double> _inlinePlaceholderScales;
-
- /// Sets the dimensions of each placeholder in [text].
- ///
- /// The number of [PlaceholderDimensions] provided should be the same as the
- /// number of [PlaceholderSpan]s in text. Passing in an empty or null `value`
- /// will do nothing.
- ///
- /// If [layout] is attempted without setting the placeholder dimensions, the
- /// placeholders will be ignored in the text layout and no valid
- /// [inlinePlaceholderBoxes] will be returned.
- void setPlaceholderDimensions(List<PlaceholderDimensions> value) {
- if (value == null || value.isEmpty || listEquals(value, _placeholderDimensions)) {
- return;
- }
- assert(() {
- int placeholderCount = 0;
- text.visitChildren((InlineSpan span) {
- if (span is PlaceholderSpan) {
- placeholderCount += 1;
- }
- return true;
- });
- return placeholderCount;
- }() == value.length);
- _placeholderDimensions = value;
- _needsLayout = true;
- _paragraph = null;
- }
- List<PlaceholderDimensions> _placeholderDimensions;
-
ui.ParagraphStyle _createParagraphStyle([ TextDirection defaultTextDirection ]) {
// The defaultTextDirection argument is used for preferredLineHeight in case
// textDirection hasn't yet been set.
@@ -533,8 +419,7 @@
_needsLayout = false;
if (_paragraph == null) {
final ui.ParagraphBuilder builder = ui.ParagraphBuilder(_createParagraphStyle());
- _text.build(builder, textScaleFactor: textScaleFactor, dimensions: _placeholderDimensions);
- _inlinePlaceholderScales = builder.placeholderScales;
+ _text.build(builder, textScaleFactor: textScaleFactor);
_paragraph = builder.build();
}
_lastMinWidth = minWidth;
@@ -542,11 +427,9 @@
_paragraph.layout(ui.ParagraphConstraints(width: maxWidth));
if (minWidth != maxWidth) {
final double newWidth = maxIntrinsicWidth.clamp(minWidth, maxWidth);
- if (newWidth != width) {
+ if (newWidth != width)
_paragraph.layout(ui.ParagraphConstraints(width: newWidth));
- }
}
- _inlinePlaceholderBoxes = _paragraph.getBoxesForPlaceholders();
}
/// Paints the text onto the given canvas at the given offset.
@@ -608,7 +491,7 @@
// TODO(garyq): Use actual extended grapheme cluster length instead of
// an increasing cluster length amount to achieve deterministic performance.
Rect _getRectFromUpstream(int offset, Rect caretPrototype) {
- final String flattenedText = _text.toPlainText(includePlaceholders: false);
+ final String flattenedText = _text.toPlainText();
final int prevCodeUnit = _text.codeUnitAt(max(0, offset - 1));
if (prevCodeUnit == null)
return null;
@@ -624,12 +507,10 @@
if (boxes.isEmpty) {
// When we are at the beginning of the line, a non-surrogate position will
// return empty boxes. We break and try from downstream instead.
- if (!needsSearch) {
+ if (!needsSearch)
break; // Only perform one iteration if no search is required.
- }
- if (prevRuneOffset < -flattenedText.length) {
+ if (prevRuneOffset < -flattenedText.length)
break; // Stop iterating when beyond the max length of the text.
- }
// Multiply by two to log(n) time cover the entire text span. This allows
// faster discovery of very long clusters and reduces the possibility
// of certain large clusters taking much longer than others, which can
@@ -657,7 +538,7 @@
// TODO(garyq): Use actual extended grapheme cluster length instead of
// an increasing cluster length amount to achieve deterministic performance.
Rect _getRectFromDownstream(int offset, Rect caretPrototype) {
- final String flattenedText = _text.toPlainText(includePlaceholders: false);
+ final String flattenedText = _text.toPlainText();
// We cap the offset at the final index of the _text.
final int nextCodeUnit = _text.codeUnitAt(min(offset, flattenedText == null ? 0 : flattenedText.length - 1));
if (nextCodeUnit == null)
@@ -673,12 +554,10 @@
if (boxes.isEmpty) {
// When we are at the end of the line, a non-surrogate position will
// return empty boxes. We break and try from upstream instead.
- if (!needsSearch) {
+ if (!needsSearch)
break; // Only perform one iteration if no search is required.
- }
- if (nextRuneOffset >= flattenedText.length << 1) {
+ if (nextRuneOffset >= flattenedText.length << 1)
break; // Stop iterating when beyond the max length of the text.
- }
// Multiply by two to log(n) time cover the entire text span. This allows
// faster discovery of very long clusters and reduces the possibility
// of certain large clusters taking much longer than others, which can
diff --git a/packages/flutter/lib/src/painting/text_span.dart b/packages/flutter/lib/src/painting/text_span.dart
index 31baf09..bba61a7 100644
--- a/packages/flutter/lib/src/painting/text_span.dart
+++ b/packages/flutter/lib/src/painting/text_span.dart
@@ -9,8 +9,6 @@
import 'package:flutter/services.dart';
import 'basic_types.dart';
-import 'inline_span.dart';
-import 'text_painter.dart';
import 'text_style.dart';
/// An immutable span of text.
@@ -23,9 +21,7 @@
/// only partially) override the [style] of this object. If a
/// [TextSpan] has both [text] and [children], then the [text] is
/// treated as if it was an unstyled [TextSpan] at the start of the
-/// [children] list. Leaving the [TextSpan.text] field null results
-/// in the [TextSpan] acting as an empty node in the [InlineSpan]
-/// tree with a list of children.
+/// [children] list.
///
/// To paint a [TextSpan] on a [Canvas], use a [TextPainter]. To display a text
/// span in a widget, use a [RichText]. For text with a single style, consider
@@ -46,33 +42,27 @@
/// _There is some more detailed sample code in the documentation for the
/// [recognizer] property._
///
-/// The [TextSpan.text] will be used as the semantics label unless overriden
-/// by the [TextSpan.semanticsLabel] property. Any [PlaceholderSpan]s in the
-/// [TextSpan.children] list will separate the text before and after it into
-/// two semantics nodes.
-///
/// See also:
///
-/// * [WidgetSpan], a leaf node that represents an embedded inline widget
-/// in an [InlineSpan] tree. Specify a widget within the [children]
-/// list by wrapping the widget with a [WidgetSpan]. The widget will be
-/// laid out inline within the paragraph.
/// * [Text], a widget for showing uniformly-styled text.
/// * [RichText], a widget for finer control of text rendering.
/// * [TextPainter], a class for painting [TextSpan] objects on a [Canvas].
@immutable
-class TextSpan extends InlineSpan {
+class TextSpan extends DiagnosticableTree {
/// Creates a [TextSpan] with the given values.
///
/// For the object to be useful, at least one of [text] or
/// [children] should be set.
const TextSpan({
+ this.style,
this.text,
this.children,
- TextStyle style,
this.recognizer,
this.semanticsLabel,
- }) : super(style: style,);
+ });
+
+ /// The style to apply to the [text] and the [children].
+ final TextStyle style;
/// The text contained in the span.
///
@@ -89,26 +79,26 @@
/// supported and may have unexpected results.
///
/// The list must not contain any nulls.
- final List<InlineSpan> children;
+ final List<TextSpan> children;
- /// A gesture recognizer that will receive events that hit this span.
+ /// A gesture recognizer that will receive events that hit this text span.
///
- /// [InlineSpan] itself does not implement hit testing or event dispatch. The
- /// object that manages the [InlineSpan] painting is also responsible for
+ /// [TextSpan] itself does not implement hit testing or event dispatch. The
+ /// object that manages the [TextSpan] painting is also responsible for
/// dispatching events. In the rendering library, that is the
/// [RenderParagraph] object, which corresponds to the [RichText] widget in
- /// the widgets layer; these objects do not bubble events in [InlineSpan]s, so a
+ /// the widgets layer; these objects do not bubble events in [TextSpan]s, so a
/// [recognizer] is only effective for events that directly hit the [text] of
- /// that [InlineSpan], not any of its [children].
+ /// that [TextSpan], not any of its [children].
///
- /// [InlineSpan] also does not manage the lifetime of the gesture recognizer.
+ /// [TextSpan] also does not manage the lifetime of the gesture recognizer.
/// The code that owns the [GestureRecognizer] object must call
- /// [GestureRecognizer.dispose] when the [InlineSpan] object is no longer used.
+ /// [GestureRecognizer.dispose] when the [TextSpan] object is no longer used.
///
/// {@tool sample}
///
/// This example shows how to manage the lifetime of a gesture recognizer
- /// provided to an [InlineSpan] object. It defines a `BuzzingText` widget which
+ /// provided to a [TextSpan] object. It defines a `BuzzingText` widget which
/// uses the [HapticFeedback] class to vibrate the device when the user
/// long-presses the "find the" span, which is underlined in wavy green. The
/// hit-testing is handled by the [RichText] widget.
@@ -141,11 +131,11 @@
///
/// @override
/// Widget build(BuildContext context) {
- /// return Text.rich(
- /// TextSpan(
+ /// return RichText(
+ /// text: TextSpan(
/// text: 'Can you ',
/// style: TextStyle(color: Colors.black),
- /// children: <InlineSpan>[
+ /// children: <TextSpan>[
/// TextSpan(
/// text: 'find the',
/// style: TextStyle(
@@ -167,7 +157,7 @@
/// {@end-tool}
final GestureRecognizer recognizer;
- /// An alternative semantics label for this [TextSpan].
+ /// An alternative semantics label for this text.
///
/// If present, the semantics of this span will contain this value instead
/// of the actual text.
@@ -187,8 +177,7 @@
/// Rather than using this directly, it's simpler to use the
/// [TextPainter] class to paint [TextSpan] objects onto [Canvas]
/// objects.
- @override
- void build(ui.ParagraphBuilder builder, { double textScaleFactor = 1.0, List<PlaceholderDimensions> dimensions }) {
+ void build(ui.ParagraphBuilder builder, { double textScaleFactor = 1.0 }) {
assert(debugAssertIsValid());
final bool hasStyle = style != null;
if (hasStyle)
@@ -196,9 +185,9 @@
if (text != null)
builder.addText(text);
if (children != null) {
- for (InlineSpan child in children) {
+ for (TextSpan child in children) {
assert(child != null);
- child.build(builder, textScaleFactor: textScaleFactor, dimensions: dimensions);
+ child.build(builder, textScaleFactor: textScaleFactor);
}
}
if (hasStyle)
@@ -207,15 +196,14 @@
/// Walks this text span and its descendants in pre-order and calls [visitor]
/// for each span that has text.
- @override
- bool visitChildren(InlineSpanVisitor visitor) {
+ bool visitTextSpan(bool visitor(TextSpan span)) {
if (text != null) {
if (!visitor(this))
return false;
}
if (children != null) {
- for (InlineSpan child in children) {
- if (!child.visitChildren(visitor))
+ for (TextSpan child in children) {
+ if (!child.visitTextSpan(visitor))
return false;
}
}
@@ -223,62 +211,63 @@
}
/// Returns the text span that contains the given position in the text.
- @override
- InlineSpan getSpanForPositionVisitor(TextPosition position, Accumulator offset) {
- if (text == null) {
- return null;
- }
+ TextSpan getSpanForPosition(TextPosition position) {
+ assert(debugAssertIsValid());
final TextAffinity affinity = position.affinity;
final int targetOffset = position.offset;
- final int endOffset = offset.value + text.length;
- if (offset.value == targetOffset && affinity == TextAffinity.downstream ||
- offset.value < targetOffset && targetOffset < endOffset ||
- endOffset == targetOffset && affinity == TextAffinity.upstream) {
- return this;
- }
- offset.increment(text.length);
- return null;
- }
-
- @override
- void computeToPlainText(StringBuffer buffer, {bool includeSemanticsLabels = true, bool includePlaceholders = true}) {
- assert(debugAssertIsValid());
- if (semanticsLabel != null && includeSemanticsLabels) {
- buffer.write(semanticsLabel);
- } else if (text != null) {
- buffer.write(text);
- }
- if (children != null) {
- for (InlineSpan child in children) {
- child.computeToPlainText(buffer,
- includeSemanticsLabels: includeSemanticsLabels,
- includePlaceholders: includePlaceholders,
- );
+ int offset = 0;
+ TextSpan result;
+ visitTextSpan((TextSpan span) {
+ assert(result == null);
+ final int endOffset = offset + span.text.length;
+ if (targetOffset == offset && affinity == TextAffinity.downstream ||
+ targetOffset > offset && targetOffset < endOffset ||
+ targetOffset == endOffset && affinity == TextAffinity.upstream) {
+ result = span;
+ return false;
}
- }
+ offset = endOffset;
+ return true;
+ });
+ return result;
}
- @override
- int codeUnitAtVisitor(int index, Accumulator offset) {
- if (text == null) {
+ /// Flattens the [TextSpan] tree into a single string.
+ ///
+ /// Styles are not honored in this process. If `includeSemanticsLabels` is
+ /// true, then the text returned will include the [semanticsLabel]s instead of
+ /// the text contents when they are present.
+ String toPlainText({bool includeSemanticsLabels = true}) {
+ assert(debugAssertIsValid());
+ final StringBuffer buffer = StringBuffer();
+ visitTextSpan((TextSpan span) {
+ if (span.semanticsLabel != null && includeSemanticsLabels) {
+ buffer.write(span.semanticsLabel);
+ } else {
+ buffer.write(span.text);
+ }
+ return true;
+ });
+ return buffer.toString();
+ }
+
+ /// Returns the UTF-16 code unit at the given index in the flattened string.
+ ///
+ /// Returns null if the index is out of bounds.
+ int codeUnitAt(int index) {
+ if (index < 0)
return null;
- }
- if (index - offset.value < text.length) {
- return text.codeUnitAt(index - offset.value);
- }
- offset.increment(text.length);
- return null;
- }
-
- @override
- void describeSemantics(Accumulator offset, List<int> semanticsOffsets, List<dynamic> semanticsElements) {
- if (recognizer != null && (recognizer is TapGestureRecognizer || recognizer is LongPressGestureRecognizer)) {
- final int length = semanticsLabel?.length ?? text.length;
- semanticsOffsets.add(offset.value);
- semanticsOffsets.add(offset.value + length);
- semanticsElements.add(recognizer);
- }
- offset.increment(text != null ? text.length : 0);
+ int offset = 0;
+ int result;
+ visitTextSpan((TextSpan span) {
+ if (index - offset < span.text.length) {
+ result = span.text.codeUnitAt(index - offset);
+ return false;
+ }
+ offset += span.text.length;
+ return true;
+ });
+ return result;
}
/// In checked mode, throws an exception if the object is not in a
@@ -289,39 +278,45 @@
/// ```dart
/// assert(myTextSpan.debugAssertIsValid());
/// ```
- @override
bool debugAssertIsValid() {
assert(() {
- if (children != null) {
- for (InlineSpan child in children) {
- assert(child != null,
- 'TextSpan contains a null child.\n...'
- 'A TextSpan object with a non-null child list should not have any nulls in its child list.\n'
- 'The full text in question was:\n'
- '${toStringDeep(prefixLineOne: ' ')}'
- );
- assert(child.debugAssertIsValid());
+ if (!visitTextSpan((TextSpan span) {
+ if (span.children != null) {
+ for (TextSpan child in span.children) {
+ if (child == null)
+ return false;
+ }
}
+ return true;
+ })) {
+ throw FlutterError(
+ 'TextSpan contains a null child.\n'
+ 'A TextSpan object with a non-null child list should not have any nulls in its child list.\n'
+ 'The full text in question was:\n'
+ '${toStringDeep(prefixLineOne: ' ')}'
+ );
}
return true;
}());
- return super.debugAssertIsValid();
+ return true;
}
- @override
- RenderComparison compareTo(InlineSpan other) {
+ /// Describe the difference between this text span and another, in terms of
+ /// how much damage it will make to the rendering. The comparison is deep.
+ ///
+ /// See also:
+ ///
+ /// * [TextStyle.compareTo], which does the same thing for [TextStyle]s.
+ RenderComparison compareTo(TextSpan other) {
if (identical(this, other))
return RenderComparison.identical;
- if (other.runtimeType != runtimeType)
+ if (other.text != text ||
+ children?.length != other.children?.length ||
+ (style == null) != (other.style == null))
return RenderComparison.layout;
- final TextSpan textSpan = other;
- if (textSpan.text != text ||
- children?.length != textSpan.children?.length ||
- (style == null) != (textSpan.style == null))
- return RenderComparison.layout;
- RenderComparison result = recognizer == textSpan.recognizer ? RenderComparison.identical : RenderComparison.metadata;
+ RenderComparison result = recognizer == other.recognizer ? RenderComparison.identical : RenderComparison.metadata;
if (style != null) {
- final RenderComparison candidate = style.compareTo(textSpan.style);
+ final RenderComparison candidate = style.compareTo(other.style);
if (candidate.index > result.index)
result = candidate;
if (result == RenderComparison.layout)
@@ -329,7 +324,7 @@
}
if (children != null) {
for (int index = 0; index < children.length; index += 1) {
- final RenderComparison candidate = children[index].compareTo(textSpan.children[index]);
+ final RenderComparison candidate = children[index].compareTo(other.children[index]);
if (candidate.index > result.index)
result = candidate;
if (result == RenderComparison.layout)
@@ -345,17 +340,16 @@
return true;
if (other.runtimeType != runtimeType)
return false;
- if (super != other)
- return false;
final TextSpan typedOther = other;
return typedOther.text == text
+ && typedOther.style == style
&& typedOther.recognizer == recognizer
&& typedOther.semanticsLabel == semanticsLabel
- && listEquals<InlineSpan>(typedOther.children, children);
+ && listEquals<TextSpan>(typedOther.children, children);
}
@override
- int get hashCode => hashValues(super.hashCode, text, recognizer, semanticsLabel, hashList(children));
+ int get hashCode => hashValues(style, text, recognizer, semanticsLabel, hashList(children));
@override
String toStringShort() => '$runtimeType';
@@ -363,10 +357,11 @@
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
-
- properties.add(StringProperty('text', text, showName: false, defaultValue: null));
- if (style == null && text == null && children == null)
- properties.add(DiagnosticsNode.message('(empty)'));
+ properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.whitespace;
+ // Properties on style are added as if they were properties directly on
+ // this TextSpan.
+ if (style != null)
+ style.debugFillProperties(properties);
properties.add(DiagnosticsProperty<GestureRecognizer>(
'recognizer', recognizer,
@@ -374,16 +369,22 @@
defaultValue: null,
));
+
if (semanticsLabel != null) {
properties.add(StringProperty('semanticsLabel', semanticsLabel));
}
+
+
+ properties.add(StringProperty('text', text, showName: false, defaultValue: null));
+ if (style == null && text == null && children == null)
+ properties.add(DiagnosticsNode.message('(empty)'));
}
@override
List<DiagnosticsNode> debugDescribeChildren() {
if (children == null)
return const <DiagnosticsNode>[];
- return children.map<DiagnosticsNode>((InlineSpan child) {
+ return children.map<DiagnosticsNode>((TextSpan child) {
if (child != null) {
return child.toDiagnosticsNode();
} else {
diff --git a/packages/flutter/lib/src/rendering/box.dart b/packages/flutter/lib/src/rendering/box.dart
index bcff74c..0b82947 100644
--- a/packages/flutter/lib/src/rendering/box.dart
+++ b/packages/flutter/lib/src/rendering/box.dart
@@ -1728,10 +1728,7 @@
return true;
}());
_size = value;
- assert(() {
- debugAssertDoesMeetConstraints();
- return true;
- }());
+ assert(() { debugAssertDoesMeetConstraints(); return true; }());
}
/// Claims ownership of the given [Size].
diff --git a/packages/flutter/lib/src/rendering/debug_overflow_indicator.dart b/packages/flutter/lib/src/rendering/debug_overflow_indicator.dart
index be8790a..d7a0d6d 100644
--- a/packages/flutter/lib/src/rendering/debug_overflow_indicator.dart
+++ b/packages/flutter/lib/src/rendering/debug_overflow_indicator.dart
@@ -284,8 +284,8 @@
final List<_OverflowRegionData> overflowRegions = _calculateOverflowRegions(overflow, containerRect);
for (_OverflowRegionData region in overflowRegions) {
context.canvas.drawRect(region.rect.shift(offset), _indicatorPaint);
- final TextSpan textSpan = _indicatorLabel[region.side.index].text;
- if (textSpan?.text != region.label) {
+
+ if (_indicatorLabel[region.side.index].text?.text != region.label) {
_indicatorLabel[region.side.index].text = TextSpan(
text: region.label,
style: _indicatorTextStyle,
diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart
index b0f44bb..151f0adf 100644
--- a/packages/flutter/lib/src/rendering/paragraph.dart
+++ b/packages/flutter/lib/src/rendering/paragraph.dart
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:ui' as ui show Gradient, Shader, TextBox, PlaceholderAlignment;
+import 'dart:ui' as ui show Gradient, Shader, TextBox;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
@@ -10,7 +10,6 @@
import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart';
-import 'package:vector_math/vector_math_64.dart';
import 'box.dart';
import 'debug.dart';
@@ -36,27 +35,8 @@
const String _kEllipsis = '\u2026';
-/// Parent data for use with [RenderParagraph].
-class TextParentData extends ContainerBoxParentData<RenderBox> {
- /// The scaling of the text.
- double scale;
-
- @override
- String toString() {
- final List<String> values = <String>[];
- if (offset != null)
- values.add('offset=$offset');
- if (scale != null)
- values.add('scale=$scale');
- values.add(super.toString());
- return values.join('; ');
- }
-}
-
-/// A render object that displays a paragraph of text.
-class RenderParagraph extends RenderBox
- with ContainerRenderObjectMixin<RenderBox, TextParentData>,
- RenderBoxContainerDefaultsMixin<RenderBox, TextParentData> {
+/// A render object that displays a paragraph of text
+class RenderParagraph extends RenderBox {
/// Creates a paragraph render object.
///
/// The [text], [textAlign], [textDirection], [overflow], [softWrap], and
@@ -64,7 +44,8 @@
///
/// The [maxLines] property may be null (and indeed defaults to null), but if
/// it is not null, it must be greater than zero.
- RenderParagraph(InlineSpan text, {
+ RenderParagraph(
+ TextSpan text, {
TextAlign textAlign = TextAlign.start,
@required TextDirection textDirection,
bool softWrap = true,
@@ -74,7 +55,6 @@
TextWidthBasis textWidthBasis = TextWidthBasis.parent,
Locale locale,
StrutStyle strutStyle,
- List<RenderBox> children,
}) : assert(text != null),
assert(text.debugAssertIsValid()),
assert(textAlign != null),
@@ -96,22 +76,13 @@
locale: locale,
strutStyle: strutStyle,
textWidthBasis: textWidthBasis,
- ) {
- addAll(children);
- _extractPlaceholderSpans(text);
- }
-
- @override
- void setupParentData(RenderBox child) {
- if (child.parentData is! TextParentData)
- child.parentData = TextParentData();
- }
+ );
final TextPainter _textPainter;
/// The text to display
- InlineSpan get text => _textPainter.text;
- set text(InlineSpan value) {
+ TextSpan get text => _textPainter.text;
+ set text(TextSpan value) {
assert(value != null);
switch (_textPainter.text.compareTo(value)) {
case RenderComparison.identical:
@@ -119,31 +90,17 @@
return;
case RenderComparison.paint:
_textPainter.text = value;
- _extractPlaceholderSpans(value);
markNeedsPaint();
markNeedsSemanticsUpdate();
break;
case RenderComparison.layout:
_textPainter.text = value;
_overflowShader = null;
- _extractPlaceholderSpans(value);
markNeedsLayout();
break;
}
}
- List<PlaceholderSpan> _placeholderSpans;
- void _extractPlaceholderSpans(InlineSpan span) {
- _placeholderSpans = <PlaceholderSpan>[];
- span.visitChildren((InlineSpan span) {
- if (span is PlaceholderSpan) {
- final PlaceholderSpan placeholderSpan = span;
- _placeholderSpans.add(placeholderSpan);
- }
- return true;
- });
- }
-
/// How the text should be aligned horizontally.
TextAlign get textAlign => _textPainter.textAlign;
set textAlign(TextAlign value) {
@@ -272,31 +229,28 @@
markNeedsLayout();
}
+ void _layoutText({ double minWidth = 0.0, double maxWidth = double.infinity }) {
+ final bool widthMatters = softWrap || overflow == TextOverflow.ellipsis;
+ _textPainter.layout(minWidth: minWidth, maxWidth: widthMatters ? maxWidth : double.infinity);
+ }
+
+ void _layoutTextWithConstraints(BoxConstraints constraints) {
+ _layoutText(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
+ }
+
@override
double computeMinIntrinsicWidth(double height) {
- if (!_canComputeIntrinsics()) {
- return 0.0;
- }
- _computeChildrenWidthWithMinIntrinsics(height);
- _layoutText(); // layout with infinite width.
+ _layoutText();
return _textPainter.minIntrinsicWidth;
}
@override
double computeMaxIntrinsicWidth(double height) {
- if (!_canComputeIntrinsics()) {
- return 0.0;
- }
- _computeChildrenWidthWithMaxIntrinsics(height);
- _layoutText(); // layout with infinite width.
+ _layoutText();
return _textPainter.maxIntrinsicWidth;
}
double _computeIntrinsicHeight(double width) {
- if (!_canComputeIntrinsics()) {
- return 0.0;
- }
- _computeChildrenHeightWithMinIntrinsics(width);
_layoutText(minWidth: width, maxWidth: width);
return _textPainter.height;
}
@@ -320,115 +274,10 @@
return _textPainter.computeDistanceToActualBaseline(baseline);
}
- // Intrinsics cannot be calculated without a full layout for
- // alignments that require the baseline (baseline, aboveBaseline,
- // belowBaseline).
- bool _canComputeIntrinsics() {
- for (PlaceholderSpan span in _placeholderSpans) {
- switch (span.alignment) {
- case ui.PlaceholderAlignment.baseline:
- case ui.PlaceholderAlignment.aboveBaseline:
- case ui.PlaceholderAlignment.belowBaseline: {
- assert(RenderObject.debugCheckingIntrinsics,
- 'Intrinsics are not available for PlaceholderAlignment.baseline, '
- 'PlaceholderAlignment.aboveBaseline, or PlaceholderAlignment.belowBaseline,');
- return false;
- }
- case ui.PlaceholderAlignment.top:
- case ui.PlaceholderAlignment.middle:
- case ui.PlaceholderAlignment.bottom: {
- continue;
- }
- }
- }
- return true;
- }
-
- void _computeChildrenWidthWithMaxIntrinsics(double height) {
- RenderBox child = firstChild;
- final List<PlaceholderDimensions> placeholderDimensions = List<PlaceholderDimensions>(childCount);
- int childIndex = 0;
- while (child != null) {
- // Height and baseline is irrelevant as all text will be laid
- // out in a single line.
- placeholderDimensions[childIndex] = PlaceholderDimensions(
- size: Size(child.getMaxIntrinsicWidth(height), height),
- alignment: _placeholderSpans[childIndex].alignment,
- baseline: _placeholderSpans[childIndex].baseline,
- );
- child = childAfter(child);
- childIndex += 1;
- }
- _textPainter.setPlaceholderDimensions(placeholderDimensions);
- }
-
- void _computeChildrenWidthWithMinIntrinsics(double height) {
- RenderBox child = firstChild;
- final List<PlaceholderDimensions> placeholderDimensions = List<PlaceholderDimensions>(childCount);
- int childIndex = 0;
- while (child != null) {
- final double intrinsicWidth = child.getMinIntrinsicWidth(height);
- final double intrinsicHeight = child.getMinIntrinsicHeight(intrinsicWidth);
- placeholderDimensions[childIndex] = PlaceholderDimensions(
- size: Size(intrinsicWidth, intrinsicHeight),
- alignment: _placeholderSpans[childIndex].alignment,
- baseline: _placeholderSpans[childIndex].baseline,
- );
- child = childAfter(child);
- childIndex += 1;
- }
- _textPainter.setPlaceholderDimensions(placeholderDimensions);
- }
-
- void _computeChildrenHeightWithMinIntrinsics(double width) {
- RenderBox child = firstChild;
- final List<PlaceholderDimensions> placeholderDimensions = List<PlaceholderDimensions>(childCount);
- int childIndex = 0;
- while (child != null) {
- final double intrinsicHeight = child.getMinIntrinsicHeight(width);
- final double intrinsicWidth = child.getMinIntrinsicWidth(intrinsicHeight);
- placeholderDimensions[childIndex] = PlaceholderDimensions(
- size: Size(intrinsicWidth, intrinsicHeight),
- alignment: _placeholderSpans[childIndex].alignment,
- baseline: _placeholderSpans[childIndex].baseline,
- );
- child = childAfter(child);
- childIndex += 1;
- }
- _textPainter.setPlaceholderDimensions(placeholderDimensions);
- }
-
@override
bool hitTestSelf(Offset position) => true;
@override
- bool hitTestChildren(BoxHitTestResult result, { Offset position }) {
- RenderBox child = firstChild;
- while (child != null) {
- final TextParentData textParentData = child.parentData;
- final Matrix4 transform = Matrix4.translationValues(textParentData.offset.dx, textParentData.offset.dy, 0.0)
- ..scale(textParentData.scale, textParentData.scale, textParentData.scale);
- final bool isHit = result.addWithPaintTransform(
- transform: transform,
- position: position,
- hitTest: (BoxHitTestResult result, Offset transformed) {
- assert(() {
- final Offset manualPosition = (position - textParentData.offset) / textParentData.scale;
- return (transformed.dx - manualPosition.dx).abs() < precisionErrorTolerance
- && (transformed.dy - manualPosition.dy).abs() < precisionErrorTolerance;
- }());
- return child.hitTest(result, position: transformed);
- },
- );
- if (isHit) {
- return true;
- }
- child = childAfter(child);
- }
- return false;
- }
-
- @override
void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
assert(debugHandleEvent(event, entry));
if (event is! PointerDownEvent)
@@ -450,81 +299,9 @@
@visibleForTesting
bool get debugHasOverflowShader => _overflowShader != null;
- void _layoutText({ double minWidth = 0.0, double maxWidth = double.infinity }) {
- final bool widthMatters = softWrap || overflow == TextOverflow.ellipsis;
- _textPainter.layout(minWidth: minWidth, maxWidth: widthMatters ? maxWidth : double.infinity);
- }
-
- void _layoutTextWithConstraints(BoxConstraints constraints) {
- _layoutText(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
- }
-
- // Layout the child inline widgets. We then pass the dimensions of the
- // children to _textPainter so that appropriate placeholders can be inserted
- // into the LibTxt layout. This does not do anything if no inline widgets were
- // specified.
- void _layoutChildren(BoxConstraints constraints) {
- if (childCount == 0) {
- return;
- }
- RenderBox child = firstChild;
- final List<PlaceholderDimensions> placeholderDimensions = List<PlaceholderDimensions>(childCount);
- int childIndex = 0;
- while (child != null) {
- // Only constrain the width to the maximum width of the paragraph.
- // Leave height unconstrained, which will overflow if expanded past.
- child.layout(
- BoxConstraints(
- maxWidth: constraints.maxWidth,
- ),
- parentUsesSize: true
- );
- double baselineOffset;
- switch (_placeholderSpans[childIndex].alignment) {
- case ui.PlaceholderAlignment.baseline: {
- baselineOffset = child.getDistanceToBaseline(_placeholderSpans[childIndex].baseline);
- break;
- }
- default: {
- baselineOffset = null;
- break;
- }
- }
- placeholderDimensions[childIndex] = PlaceholderDimensions(
- size: child.size,
- alignment: _placeholderSpans[childIndex].alignment,
- baseline: _placeholderSpans[childIndex].baseline,
- baselineOffset: baselineOffset,
- );
- child = childAfter(child);
- childIndex += 1;
- }
- _textPainter.setPlaceholderDimensions(placeholderDimensions);
- }
-
- // Iterate through the laid-out children and set the parentData offsets based
- // off of the placeholders inserted for each child.
- void _setParentData() {
- RenderBox child = firstChild;
- int childIndex = 0;
- while (child != null) {
- final TextParentData textParentData = child.parentData;
- textParentData.offset = Offset(
- _textPainter.inlinePlaceholderBoxes[childIndex].left,
- _textPainter.inlinePlaceholderBoxes[childIndex].top
- );
- textParentData.scale = _textPainter.inlinePlaceholderScales[childIndex];
- child = childAfter(child);
- childIndex += 1;
- }
- }
-
@override
void performLayout() {
- _layoutChildren(constraints);
_layoutTextWithConstraints(constraints);
- _setParentData();
-
// We grab _textPainter.size and _textPainter.didExceedMaxLines here because
// assigning to `size` will trigger us to validate our intrinsic sizes,
// which will change _textPainter's layout because the intrinsic size
@@ -609,12 +386,13 @@
// If you remove this call, make sure that changing the textAlign still
// works properly.
_layoutTextWithConstraints(constraints);
+ final Canvas canvas = context.canvas;
assert(() {
if (debugRepaintTextRainbowEnabled) {
final Paint paint = Paint()
..color = debugCurrentRepaintColor.toColor();
- context.canvas.drawRect(offset & size, paint);
+ canvas.drawRect(offset & size, paint);
}
return true;
}());
@@ -624,44 +402,22 @@
if (_overflowShader != null) {
// This layer limits what the shader below blends with to be just the text
// (as opposed to the text and its background).
- context.canvas.saveLayer(bounds, Paint());
+ canvas.saveLayer(bounds, Paint());
} else {
- context.canvas.save();
+ canvas.save();
}
- context.canvas.clipRect(bounds);
+ canvas.clipRect(bounds);
}
- _textPainter.paint(context.canvas, offset);
-
- RenderBox child = firstChild;
- int childIndex = 0;
- while (child != null) {
- assert(childIndex < _textPainter.inlinePlaceholderBoxes.length);
- final TextParentData textParentData = child.parentData;
-
- final double scale = textParentData.scale;
- context.pushTransform(
- needsCompositing,
- offset + textParentData.offset,
- Matrix4.diagonal3Values(scale, scale, scale),
- (PaintingContext context, Offset offset) {
- context.paintChild(
- child,
- offset,
- );
- },
- );
- child = childAfter(child);
- childIndex += 1;
- }
+ _textPainter.paint(canvas, offset);
if (_needsClipping) {
if (_overflowShader != null) {
- context.canvas.translate(offset.dx, offset.dy);
+ canvas.translate(offset.dx, offset.dy);
final Paint paint = Paint()
..blendMode = BlendMode.modulate
..shader = _overflowShader;
- context.canvas.drawRect(Offset.zero & size, paint);
+ canvas.drawRect(Offset.zero & size, paint);
}
- context.canvas.restore();
+ canvas.restore();
}
}
@@ -725,23 +481,26 @@
return _textPainter.size;
}
- // The offsets for each span that requires custom semantics.
- final List<int> _inlineSemanticsOffsets = <int>[];
- // Holds either [GestureRecognizer] or null (for placeholders) to generate
- // proper semnatics configurations.
- final List<dynamic> _inlineSemanticsElements = <dynamic>[];
+ final List<int> _recognizerOffsets = <int>[];
+ final List<GestureRecognizer> _recognizers = <GestureRecognizer>[];
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
- _inlineSemanticsOffsets.clear();
- _inlineSemanticsElements.clear();
- final Accumulator offset = Accumulator();
- text.visitChildren((InlineSpan span) {
- span.describeSemantics(offset, _inlineSemanticsOffsets, _inlineSemanticsElements);
+ _recognizerOffsets.clear();
+ _recognizers.clear();
+ int offset = 0;
+ text.visitTextSpan((TextSpan span) {
+ if (span.recognizer != null && (span.recognizer is TapGestureRecognizer || span.recognizer is LongPressGestureRecognizer)) {
+ final int length = span.semanticsLabel?.length ?? span.text.length;
+ _recognizerOffsets.add(offset);
+ _recognizerOffsets.add(offset + length);
+ _recognizers.add(span.recognizer);
+ }
+ offset += span.text.length;
return true;
});
- if (_inlineSemanticsOffsets.isNotEmpty) {
+ if (_recognizerOffsets.isNotEmpty) {
config.explicitChildNodes = true;
config.isSemanticBoundary = true;
} else {
@@ -752,9 +511,10 @@
@override
void assembleSemanticsNode(SemanticsNode node, SemanticsConfiguration config, Iterable<SemanticsNode> children) {
- assert(_inlineSemanticsOffsets.isNotEmpty);
- assert(_inlineSemanticsOffsets.length.isEven);
- assert(_inlineSemanticsElements.isNotEmpty);
+ assert(_recognizerOffsets.isNotEmpty);
+ assert(_recognizerOffsets.length.isEven);
+ assert(_recognizers.isNotEmpty);
+ assert(children.isEmpty);
final List<SemanticsNode> newChildren = <SemanticsNode>[];
final String rawLabel = text.toPlainText();
int current = 0;
@@ -762,7 +522,7 @@
TextDirection currentDirection = textDirection;
Rect currentRect;
- SemanticsConfiguration buildSemanticsConfig(int start, int end, { bool includeText = true }) {
+ SemanticsConfiguration buildSemanticsConfig(int start, int end) {
final TextDirection initialDirection = currentDirection;
final TextSelection selection = TextSelection(baseOffset: start, extentOffset: end);
final List<ui.TextBox> rects = getBoxesForSelection(selection);
@@ -782,21 +542,15 @@
rect.bottom.ceilToDouble() + 4.0,
);
order += 1;
- final SemanticsConfiguration configuration = SemanticsConfiguration()
+ return SemanticsConfiguration()
..sortKey = OrdinalSortKey(order)
- ..textDirection = initialDirection;
- if (includeText) {
- configuration.label = rawLabel.substring(start, end);
- }
- return configuration;
+ ..textDirection = initialDirection
+ ..label = rawLabel.substring(start, end);
}
- int childIndex = 0;
- RenderBox child = firstChild;
- for (int i = 0, j = 0; i < _inlineSemanticsOffsets.length; i += 2, j++) {
- final int start = _inlineSemanticsOffsets[i];
- final int end = _inlineSemanticsOffsets[i + 1];
- // Add semantics for any text between the previous recognizer/widget and this one.
+ for (int i = 0, j = 0; i < _recognizerOffsets.length; i += 2, j++) {
+ final int start = _recognizerOffsets[i];
+ final int end = _recognizerOffsets[i + 1];
if (current != start) {
final SemanticsNode node = SemanticsNode();
final SemanticsConfiguration configuration = buildSemanticsConfig(current, start);
@@ -804,38 +558,19 @@
node.rect = currentRect;
newChildren.add(node);
}
- final dynamic inlineElement = _inlineSemanticsElements[j];
- final SemanticsConfiguration configuration = buildSemanticsConfig(start, end, includeText: false);
- if (inlineElement != null) {
- // Add semantics for this recognizer.
- final SemanticsNode node = SemanticsNode();
- if (inlineElement is TapGestureRecognizer) {
- final TapGestureRecognizer recognizer = inlineElement;
- configuration.onTap = recognizer.onTap;
- } else if (inlineElement is LongPressGestureRecognizer) {
- final LongPressGestureRecognizer recognizer = inlineElement;
- configuration.onLongPress = recognizer.onLongPress;
- } else {
- assert(false);
- }
- node.updateWith(config: configuration);
- node.rect = currentRect;
- newChildren.add(node);
- } else if (childIndex < children.length) {
- // Add semantics for this placeholder. Semantics are precomputed in the children
- // argument.
- final SemanticsNode childNode = children.elementAt(childIndex);
- final TextParentData parentData = child.parentData;
- childNode.rect = Rect.fromLTWH(
- childNode.rect.left,
- childNode.rect.top,
- childNode.rect.width * parentData.scale,
- childNode.rect.height * parentData.scale,
- );
- newChildren.add(children.elementAt(childIndex));
- childIndex += 1;
- child = childAfter(child);
+ final SemanticsNode node = SemanticsNode();
+ final SemanticsConfiguration configuration = buildSemanticsConfig(start, end);
+ final GestureRecognizer recognizer = _recognizers[j];
+ if (recognizer is TapGestureRecognizer) {
+ configuration.onTap = recognizer.onTap;
+ } else if (recognizer is LongPressGestureRecognizer) {
+ configuration.onLongPress = recognizer.onLongPress;
+ } else {
+ assert(false);
}
+ node.updateWith(config: configuration);
+ node.rect = currentRect;
+ newChildren.add(node);
current = end;
}
if (current < rawLabel.length) {
diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart
index 596b883..e627b3e 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -12,7 +12,6 @@
import 'debug.dart';
import 'framework.dart';
import 'localizations.dart';
-import 'widget_span.dart';
export 'package:flutter/animation.dart';
export 'package:flutter/foundation.dart' show
@@ -4914,9 +4913,7 @@
/// * [TextSpan], which is used to describe the text in a paragraph.
/// * [Text], which automatically applies the ambient styles described by a
/// [DefaultTextStyle] to a single string.
-/// * [Text.rich], a const text widget that provides similar functionality
-/// as [RichText]. [Text.rich] will inherit [TextStyle] from [DefaultTextStyle].
-class RichText extends MultiChildRenderObjectWidget {
+class RichText extends LeafRenderObjectWidget {
/// Creates a paragraph of rich text.
///
/// The [text], [textAlign], [softWrap], [overflow], and [textScaleFactor]
@@ -4927,7 +4924,7 @@
///
/// The [textDirection], if null, defaults to the ambient [Directionality],
/// which in that case must not be null.
- RichText({
+ const RichText({
Key key,
@required this.text,
this.textAlign = TextAlign.start,
@@ -4946,23 +4943,10 @@
assert(textScaleFactor != null),
assert(maxLines == null || maxLines > 0),
assert(textWidthBasis != null),
- super(key: key, children: _extractChildren(text));
-
- // Traverses the InlineSpan tree and depth-first collects the list of
- // child widgets that are created in WidgetSpans.
- static List<Widget> _extractChildren(InlineSpan span) {
- final List<Widget> result = <Widget>[];
- span.visitChildren((InlineSpan span) {
- if (span is WidgetSpan) {
- result.add(span.child);
- }
- return true;
- });
- return result;
- }
+ super(key: key);
/// The text to display in this widget.
- final InlineSpan text;
+ final TextSpan text;
/// How the text should be aligned horizontally.
final TextAlign textAlign;
diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart
index 18637dd..ee3233f 100644
--- a/packages/flutter/lib/src/widgets/text.dart
+++ b/packages/flutter/lib/src/widgets/text.dart
@@ -256,16 +256,9 @@
textSpan = null,
super(key: key);
- /// 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.
+ /// Creates a text widget with a [TextSpan].
///
/// The [textSpan] parameter must not be null.
- ///
- /// See [RichText] which provides a lower-level way to draw text.
const Text.rich(
this.textSpan, {
Key key,
@@ -292,10 +285,10 @@
/// This will be null if a [textSpan] is provided instead.
final String data;
- /// The text to display as a [InlineSpan].
+ /// The text to display as a [TextSpan].
///
/// This will be null if [data] is provided instead.
- final InlineSpan textSpan;
+ final TextSpan textSpan;
/// If non-null, the style to use for this text.
///
diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart
index ab9a1be..ab178a0 100644
--- a/packages/flutter/lib/src/widgets/widget_inspector.dart
+++ b/packages/flutter/lib/src/widgets/widget_inspector.dart
@@ -2752,8 +2752,7 @@
) {
canvas.save();
final double maxWidth = size.width - 2 * (_kScreenEdgeMargin + _kTooltipPadding);
- final TextSpan textSpan = _textPainter?.text;
- if (_textPainter == null || textSpan.text != message || _textPainterMaxWidth != maxWidth) {
+ if (_textPainter == null || _textPainter.text.text != message || _textPainterMaxWidth != maxWidth) {
_textPainterMaxWidth = maxWidth;
_textPainter = TextPainter()
..maxLines = _kMaxTooltipLines
diff --git a/packages/flutter/lib/src/widgets/widget_span.dart b/packages/flutter/lib/src/widgets/widget_span.dart
deleted file mode 100644
index a24c04a..0000000
--- a/packages/flutter/lib/src/widgets/widget_span.dart
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:ui' as ui show ParagraphBuilder, PlaceholderAlignment;
-
-import 'package:flutter/painting.dart';
-
-import 'framework.dart';
-
-/// An immutable widget that is embedded inline within text.
-///
-/// The [child] property is the widget that will be embedded. Children are
-/// constrained by the width of the paragraph.
-///
-/// The [child] property may contain its own [Widget] children (if applicable),
-/// including [Text] and [RichText] widgets which may include additional
-/// [WidgetSpan]s. Child [Text] and [RichText] widgets will be laid out
-/// independently and occupy a rectangular space in the parent text layout.
-///
-/// [WidgetSpan]s will be ignored when passed into a [TextPainter] directly.
-/// To properly layout and paint the [child] widget, [WidgetSpan] should be
-/// passed into a [Text.rich] widget.
-///
-/// {@tool sample}
-///
-/// A card with `Hello World!` embedded inline within a TextSpan tree.
-///
-/// ```dart
-/// Text.rich(
-/// TextSpan(
-/// children: <InlineSpan>[
-/// TextSpan(text: 'Flutter is'),
-/// WidgetSpan(
-/// child: SizedBox(
-/// width: 120,
-/// height: 50,
-/// child: Card(
-/// child: Center(
-/// child: Text('Hello World!')
-/// )
-/// ),
-/// )
-/// ),
-/// TextSpan(text: 'the best!'),
-/// ],
-/// )
-/// )
-/// ```
-/// {@end-tool}
-///
-/// [WidgetSpan] contributes the semantics of the [WidgetSpan.child] to the
-/// semantics tree.
-///
-/// See also:
-///
-/// * [TextSpan], a node that represents text in an [InlineSpan] tree.
-/// * [Text], a widget for showing uniformly-styled text.
-/// * [RichText], a widget for finer control of text rendering.
-/// * [TextPainter], a class for painting [InlineSpan] objects on a [Canvas].
-@immutable
-class WidgetSpan extends PlaceholderSpan {
- /// Creates a [WidgetSpan] with the given values.
- ///
- /// The [child] property must be non-null. [WidgetSpan] is a leaf node in
- /// the [InlineSpan] tree. Child widgets are constrained by the width of the
- /// paragraph they occupy. Child widget heights are unconstrained, and may
- /// cause the text to overflow and be ellipsized/truncated.
- ///
- /// A [TextStyle] may be provided with the [style] property, but only the
- /// decoration, foreground, background, and spacing options will be used.
- const WidgetSpan({
- @required this.child,
- ui.PlaceholderAlignment alignment = ui.PlaceholderAlignment.bottom,
- TextBaseline baseline,
- TextStyle style,
- }) : assert(child != null),
- assert((identical(alignment, ui.PlaceholderAlignment.aboveBaseline) ||
- identical(alignment, ui.PlaceholderAlignment.belowBaseline) ||
- identical(alignment, ui.PlaceholderAlignment.baseline)) ? baseline != null : true),
- super(
- alignment: alignment,
- baseline: baseline,
- style: style,
- );
-
- /// The widget to embed inline within text.
- final Widget child;
-
- /// Adds a placeholder box to the paragraph builder if a size has been
- /// calculated for the widget.
- ///
- /// Sizes are provided through `dimensions`, which should contain a 1:1
- /// in-order mapping of widget to laid-out dimensions. If no such dimension
- /// is provided, the widget will be skipped.
- ///
- /// The `textScaleFactor` will be applied to the laid-out size of the widget.
- @override
- void build(ui.ParagraphBuilder builder, { double textScaleFactor = 1.0, @required List<PlaceholderDimensions> dimensions }) {
- assert(debugAssertIsValid());
- assert(dimensions != null);
- final bool hasStyle = style != null;
- if (hasStyle) {
- builder.pushStyle(style.getTextStyle(textScaleFactor: textScaleFactor));
- }
- assert(builder.placeholderCount < dimensions.length);
- final PlaceholderDimensions currentDimensions = dimensions[builder.placeholderCount];
- builder.addPlaceholder(
- currentDimensions.size.width,
- currentDimensions.size.height,
- alignment,
- scale: textScaleFactor,
- baseline: currentDimensions.baseline,
- baselineOffset: currentDimensions.baselineOffset,
- );
- if (hasStyle) {
- builder.pop();
- }
- }
-
- /// Calls `visitor` on this [WidgetSpan]. There are no children spans to walk.
- @override
- bool visitChildren(InlineSpanVisitor visitor) {
- return visitor(this);
- }
-
- @override
- InlineSpan getSpanForPositionVisitor(TextPosition position, Accumulator offset) {
- return null;
- }
-
- @override
- int codeUnitAtVisitor(int index, Accumulator offset) {
- return null;
- }
-
- @override
- RenderComparison compareTo(InlineSpan other) {
- if (identical(this, other))
- return RenderComparison.identical;
- if (other.runtimeType != runtimeType)
- return RenderComparison.layout;
- if ((style == null) != (other.style == null))
- return RenderComparison.layout;
- final WidgetSpan typedOther = other;
- if (child != typedOther.child || alignment != typedOther.alignment) {
- return RenderComparison.layout;
- }
- RenderComparison result = RenderComparison.identical;
- if (style != null) {
- final RenderComparison candidate = style.compareTo(other.style);
- if (candidate.index > result.index)
- result = candidate;
- if (result == RenderComparison.layout)
- return result;
- }
- return result;
- }
-
- @override
- bool operator ==(dynamic other) {
- if (identical(this, other))
- return true;
- if (other.runtimeType != runtimeType)
- return false;
- if (super != other)
- return false;
- final WidgetSpan typedOther = other;
- return typedOther.child == child
- && typedOther.alignment == alignment
- && typedOther.baseline == baseline;
- }
-
- @override
- int get hashCode => hashValues(super.hashCode, child, alignment, baseline);
-
- /// Returns the text span that contains the given position in the text.
- @override
- InlineSpan getSpanForPosition(TextPosition position) {
- assert(debugAssertIsValid());
- return null;
- }
-
- /// In debug mode, throws an exception if the object is not in a
- /// valid configuration. Otherwise, returns true.
- ///
- /// This is intended to be used as follows:
- ///
- /// ```dart
- /// assert(myWidgetSpan.debugAssertIsValid());
- /// ```
- @override
- bool debugAssertIsValid() {
- // WidgetSpans are always valid as asserts prevent invalid WidgetSpans
- // from being constructed.
- return true;
- }
-}
diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart
index d0871cf..744a3fb 100644
--- a/packages/flutter/lib/widgets.dart
+++ b/packages/flutter/lib/widgets.dart
@@ -108,5 +108,4 @@
export 'src/widgets/viewport.dart';
export 'src/widgets/visibility.dart';
export 'src/widgets/widget_inspector.dart';
-export 'src/widgets/widget_span.dart';
export 'src/widgets/will_pop_scope.dart';
diff --git a/packages/flutter/test/painting/text_painter_rtl_test.dart b/packages/flutter/test/painting/text_painter_rtl_test.dart
index e44cfe0..5f3f02b 100644
--- a/packages/flutter/test/painting/text_painter_rtl_test.dart
+++ b/packages/flutter/test/painting/text_painter_rtl_test.dart
@@ -43,8 +43,7 @@
// 0 12345678 9 101234567 18 90123456 27
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
- TextSpan textSpan = painter.text;
- expect(textSpan.text.length, 28);
+ expect(painter.text.text.length, 28);
painter.layout();
// The skips here are because the old rendering code considers the bidi formatting characters
@@ -128,8 +127,7 @@
);
final List<List<TextBox>> list = <List<TextBox>>[];
- textSpan = painter.text;
- for (int index = 0; index < textSpan.text.length; index += 1)
+ for (int index = 0; index < painter.text.text.length; index += 1)
list.add(painter.getBoxesForSelection(TextSelection(baseOffset: index, extentOffset: index + 1)));
expect(list, const <List<TextBox>>[
<TextBox>[], // U+202E, non-printing Unicode bidi formatting character
@@ -174,8 +172,7 @@
// 0 12345678 9 101234567 18 90123456 27
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
- final TextSpan textSpan = painter.text;
- expect(textSpan.text.length, 28);
+ expect(painter.text.text.length, 28);
painter.layout();
final TextRange hebrew1 = painter.getWordBoundary(const TextPosition(offset: 4, affinity: TextAffinity.downstream));
@@ -264,8 +261,7 @@
text: 'A\u05D0', // A, Alef
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
- final TextSpan textSpan = painter.text;
- expect(textSpan.text.length, 2);
+ expect(painter.text.text.length, 2);
painter.layout(maxWidth: 10.0);
for (int index = 0; index <= 2; index += 1) {
diff --git a/packages/flutter/test/painting/text_painter_test.dart b/packages/flutter/test/painting/text_painter_test.dart
index cfa5f56..80d0c22 100644
--- a/packages/flutter/test/painting/text_painter_test.dart
+++ b/packages/flutter/test/painting/text_painter_test.dart
@@ -5,7 +5,6 @@
import 'dart:ui' as ui;
import 'package:flutter/painting.dart';
-import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
@@ -635,98 +634,4 @@
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(0.0, 0.0001));
});
-
- test('TextPainter widget span', () {
- final TextPainter painter = TextPainter()
- ..textDirection = TextDirection.ltr;
-
- const String text = 'test';
- painter.text = const TextSpan(
- text: text,
- children: <InlineSpan>[
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- TextSpan(text: text),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- TextSpan(text: text),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- WidgetSpan(child: SizedBox(width: 50, height: 30)),
- ]
- );
-
- // We provide dimensions for the widgets
- painter.setPlaceholderDimensions(const <PlaceholderDimensions>[
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(51, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
- ]);
-
- painter.layout(maxWidth: 500);
-
- // Now, each of the WidgetSpans will have their own placeholder 'hole'.
- Offset caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
- expect(caretOffset.dx, 14);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 4), ui.Rect.zero);
- expect(caretOffset.dx, 56);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 5), ui.Rect.zero);
- expect(caretOffset.dx, 106);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 6), ui.Rect.zero);
- expect(caretOffset.dx, 120);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 10), ui.Rect.zero);
- expect(caretOffset.dx, 212);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 11), ui.Rect.zero);
- expect(caretOffset.dx, 262);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 12), ui.Rect.zero);
- expect(caretOffset.dx, 276);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 13), ui.Rect.zero);
- expect(caretOffset.dx, 290);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 14), ui.Rect.zero);
- expect(caretOffset.dx, 304);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 15), ui.Rect.zero);
- expect(caretOffset.dx, 318);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 16), ui.Rect.zero);
- expect(caretOffset.dx, 368);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 17), ui.Rect.zero);
- expect(caretOffset.dx, 418);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 18), ui.Rect.zero);
- expect(caretOffset.dx, 0);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 19), ui.Rect.zero);
- expect(caretOffset.dx, 50);
- caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 23), ui.Rect.zero);
- expect(caretOffset.dx, 250);
-
- expect(painter.inlinePlaceholderBoxes.length, 14);
- expect(painter.inlinePlaceholderBoxes[0], const TextBox.fromLTRBD(56, 0, 106, 30, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[2], const TextBox.fromLTRBD(212, 0, 262, 30, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[3], const TextBox.fromLTRBD(318, 0, 368, 30, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[4], const TextBox.fromLTRBD(368, 0, 418, 30, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[5], const TextBox.fromLTRBD(418, 0, 468, 30, TextDirection.ltr));
- // line should break here
- expect(painter.inlinePlaceholderBoxes[6], const TextBox.fromLTRBD(0, 30, 50, 60, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[7], const TextBox.fromLTRBD(50, 30, 100, 60, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[10], const TextBox.fromLTRBD(200, 30, 250, 60, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[11], const TextBox.fromLTRBD(250, 30, 300, 60, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[12], const TextBox.fromLTRBD(300, 30, 351, 60, TextDirection.ltr));
- expect(painter.inlinePlaceholderBoxes[13], const TextBox.fromLTRBD(351, 30, 401, 60, TextDirection.ltr));
- });
}
diff --git a/packages/flutter/test/painting/text_span_test.dart b/packages/flutter/test/painting/text_span_test.dart
index 2f7e975..072ea5f 100644
--- a/packages/flutter/test/painting/text_span_test.dart
+++ b/packages/flutter/test/painting/text_span_test.dart
@@ -3,17 +3,17 @@
// found in the LICENSE file.
import 'package:flutter/painting.dart';
-import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart' show nonconst;
import '../flutter_test_alternative.dart';
void main() {
test('TextSpan equals', () {
- const TextSpan a1 = TextSpan(text: 'a');
- const TextSpan a2 = TextSpan(text: 'a');
- const TextSpan b1 = TextSpan(children: <TextSpan>[ a1 ]);
- const TextSpan b2 = TextSpan(children: <TextSpan>[ a2 ]);
- const TextSpan c1 = TextSpan(text: null);
- const TextSpan c2 = TextSpan(text: null);
+ final TextSpan a1 = TextSpan(text: nonconst('a'));
+ final TextSpan a2 = TextSpan(text: nonconst('a'));
+ final TextSpan b1 = TextSpan(children: <TextSpan>[ a1 ]);
+ final TextSpan b2 = TextSpan(children: <TextSpan>[ a2 ]);
+ final TextSpan c1 = TextSpan(text: nonconst(null));
+ final TextSpan c2 = TextSpan(text: nonconst(null));
expect(a1 == a2, isTrue);
expect(b1 == b2, isTrue);
@@ -73,18 +73,6 @@
expect(textSpan.toPlainText(), 'abc');
});
- test('WidgetSpan toPlainText', () {
- const TextSpan textSpan = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(child: SizedBox(width: 10, height: 10)),
- TextSpan(text: 'c'),
- ],
- );
- expect(textSpan.toPlainText(), 'ab\uFFFCc');
- });
-
test('TextSpan toPlainText with semanticsLabel', () {
const TextSpan textSpan = TextSpan(
text: 'a',
@@ -96,117 +84,4 @@
expect(textSpan.toPlainText(), 'afooc');
expect(textSpan.toPlainText(includeSemanticsLabels: false), 'abc');
});
-
- test('TextSpan widget change test', () {
- const TextSpan textSpan1 = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(child: SizedBox(width: 10, height: 10)),
- TextSpan(text: 'c'),
- ],
- );
-
- const TextSpan textSpan2 = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(child: SizedBox(width: 10, height: 10)),
- TextSpan(text: 'c'),
- ],
- );
-
- const TextSpan textSpan3 = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(child: SizedBox(width: 11, height: 10)),
- TextSpan(text: 'c'),
- ],
- );
-
- const TextSpan textSpan4 = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(child: Text('test')),
- TextSpan(text: 'c'),
- ],
- );
-
- const TextSpan textSpan5 = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(child: Text('different!')),
- TextSpan(text: 'c'),
- ],
- );
-
- const TextSpan textSpan6 = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(
- child: SizedBox(width: 10, height: 10),
- alignment: PlaceholderAlignment.top,
- ),
- TextSpan(text: 'c'),
- ],
- );
-
- expect(textSpan1.compareTo(textSpan3), RenderComparison.layout);
- expect(textSpan1.compareTo(textSpan4), RenderComparison.layout);
- expect(textSpan1.compareTo(textSpan1), RenderComparison.identical);
- expect(textSpan2.compareTo(textSpan2), RenderComparison.identical);
- expect(textSpan3.compareTo(textSpan3), RenderComparison.identical);
- expect(textSpan2.compareTo(textSpan3), RenderComparison.layout);
- expect(textSpan4.compareTo(textSpan5), RenderComparison.layout);
- expect(textSpan3.compareTo(textSpan5), RenderComparison.layout);
- expect(textSpan2.compareTo(textSpan5), RenderComparison.layout);
- expect(textSpan1.compareTo(textSpan5), RenderComparison.layout);
- expect(textSpan1.compareTo(textSpan6), RenderComparison.layout);
- });
-
- test('TextSpan nested widget change test', () {
- const TextSpan textSpan1 = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(
- child: Text.rich(
- TextSpan(
- children: <InlineSpan>[
- WidgetSpan(child: SizedBox(width: 10, height: 10)),
- TextSpan(text: 'The sky is falling :)')
- ],
- )
- ),
- ),
- TextSpan(text: 'c'),
- ],
- );
-
- const TextSpan textSpan2 = TextSpan(
- text: 'a',
- children: <InlineSpan>[
- TextSpan(text: 'b'),
- WidgetSpan(
- child: Text.rich(
- TextSpan(
- children: <InlineSpan>[
- WidgetSpan(child: SizedBox(width: 10, height: 11)),
- TextSpan(text: 'The sky is falling :)')
- ],
- )
- ),
- ),
- TextSpan(text: 'c'),
- ],
- );
-
- expect(textSpan1.compareTo(textSpan2), RenderComparison.layout);
- expect(textSpan1.compareTo(textSpan1), RenderComparison.identical);
- expect(textSpan2.compareTo(textSpan2), RenderComparison.identical);
- });
}
diff --git a/packages/flutter/test/rendering/paragraph_test.dart b/packages/flutter/test/rendering/paragraph_test.dart
index 356b6d8..c998d4e 100644
--- a/packages/flutter/test/rendering/paragraph_test.dart
+++ b/packages/flutter/test/rendering/paragraph_test.dart
@@ -5,7 +5,6 @@
import 'dart:ui' as ui show TextBox;
import 'package:flutter/rendering.dart';
-import 'package:flutter/widgets.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -325,93 +324,4 @@
expect(paragraph.locale, const Locale('ja', 'JP'));
});
- test('inline widgets test', () {
- const TextSpan text = TextSpan(
- text: 'a',
- style: TextStyle(fontSize: 10.0),
- children: <InlineSpan>[
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- TextSpan(text: 'a'),
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- ],
- );
- // Fake the render boxes that correspond to the WidgetSpans. We use
- // RenderParagraph to reduce dependencies this test has.
- final List<RenderBox> renderBoxes = <RenderBox>[];
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
-
- final RenderParagraph paragraph = RenderParagraph(
- text,
- textDirection: TextDirection.ltr,
- children: renderBoxes,
- );
- layout(paragraph, constraints: const BoxConstraints(maxWidth: 100.0));
-
- final List<ui.TextBox> boxes = paragraph.getBoxesForSelection(
- const TextSelection(baseOffset: 0, extentOffset: 8)
- );
-
- expect(boxes.length, equals(5));
- expect(boxes[0], const TextBox.fromLTRBD(0.0, 4.0, 10.0, 14.0, TextDirection.ltr));
- expect(boxes[1], const TextBox.fromLTRBD(10.0, 0.0, 24.0, 14.0, TextDirection.ltr));
- expect(boxes[2], const TextBox.fromLTRBD(24.0, 0.0, 38.0, 14.0, TextDirection.ltr));
- expect(boxes[3], const TextBox.fromLTRBD(38.0, 4.0, 48.0, 14.0, TextDirection.ltr));
- expect(boxes[4], const TextBox.fromLTRBD(48.0, 0.0, 62.0, 14.0, TextDirection.ltr));
- // Ahem-based tests don't yet quite work on Windows or some MacOS environments
- }, skip: isWindows || isMacOS);
-
- test('inline widgets multiline test', () {
- const TextSpan text = TextSpan(
- text: 'a',
- style: TextStyle(fontSize: 10.0),
- children: <InlineSpan>[
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- TextSpan(text: 'a'),
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- WidgetSpan(child: SizedBox(width: 21, height: 21)),
- ],
- );
- // Fake the render boxes that correspond to the WidgetSpans. We use
- // RenderParagraph to reduce dependencies this test has.
- final List<RenderBox> renderBoxes = <RenderBox>[];
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
- renderBoxes.add(RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr));
-
- final RenderParagraph paragraph = RenderParagraph(
- text,
- textDirection: TextDirection.ltr,
- children: renderBoxes,
- );
- layout(paragraph, constraints: const BoxConstraints(maxWidth: 50.0));
-
- final List<ui.TextBox> boxes = paragraph.getBoxesForSelection(
- const TextSelection(baseOffset: 0, extentOffset: 12)
- );
-
- expect(boxes.length, equals(9));
- expect(boxes[0], const TextBox.fromLTRBD(0.0, 4.0, 10.0, 14.0, TextDirection.ltr));
- expect(boxes[1], const TextBox.fromLTRBD(10.0, 0.0, 24.0, 14.0, TextDirection.ltr));
- expect(boxes[2], const TextBox.fromLTRBD(24.0, 0.0, 38.0, 14.0, TextDirection.ltr));
- expect(boxes[3], const TextBox.fromLTRBD(38.0, 4.0, 48.0, 14.0, TextDirection.ltr));
- // Wraps
- expect(boxes[4], const TextBox.fromLTRBD(0.0, 14.0, 14.0, 28.0 , TextDirection.ltr));
- expect(boxes[5], const TextBox.fromLTRBD(14.0, 14.0, 28.0, 28.0, TextDirection.ltr));
- expect(boxes[6], const TextBox.fromLTRBD(28.0, 14.0, 42.0, 28.0, TextDirection.ltr));
- // Wraps
- expect(boxes[7], const TextBox.fromLTRBD(0.0, 28.0, 14.0, 42.0, TextDirection.ltr));
- expect(boxes[8], const TextBox.fromLTRBD(14.0, 28.0, 28.0, 42.0 , TextDirection.ltr));
- // Ahem-based tests don't yet quite work on Windows or some MacOS environments
- }, skip: isWindows || isMacOS);
}
diff --git a/packages/flutter/test/widgets/backdrop_filter_test.dart b/packages/flutter/test/widgets/backdrop_filter_test.dart
index aa27ee3..c0cff73 100644
--- a/packages/flutter/test/widgets/backdrop_filter_test.dart
+++ b/packages/flutter/test/widgets/backdrop_filter_test.dart
@@ -10,7 +10,6 @@
void main() {
testWidgets('BackdropFilter\'s cull rect does not shrink', (WidgetTester tester) async {
- tester.binding.addTime(const Duration(seconds: 15));
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
diff --git a/packages/flutter/test/widgets/basic_test.dart b/packages/flutter/test/widgets/basic_test.dart
index 15c6996..41e218c 100644
--- a/packages/flutter/test/widgets/basic_test.dart
+++ b/packages/flutter/test/widgets/basic_test.dart
@@ -8,6 +8,7 @@
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
+
void main() {
group('PhysicalShape', () {
testWidgets('properties', (WidgetTester tester) async {
diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart
index 015795c..ec9748b 100644
--- a/packages/flutter/test/widgets/editable_text_test.dart
+++ b/packages/flutter/test/widgets/editable_text_test.dart
@@ -1870,9 +1870,8 @@
final RenderEditable renderEditable = findRenderEditable(tester);
// The actual text span is split into 3 parts with the middle part underlined.
expect(renderEditable.text.children.length, 3);
- final TextSpan textSpan = renderEditable.text.children[1];
- expect(textSpan.text, 'composing');
- expect(textSpan.style.decoration, TextDecoration.underline);
+ expect(renderEditable.text.children[1].text, 'composing');
+ expect(renderEditable.text.children[1].style.decoration, TextDecoration.underline);
focusNode.unfocus();
await tester.pump();
diff --git a/packages/flutter/test/widgets/text_golden_test.dart b/packages/flutter/test/widgets/text_golden_test.dart
index bdc0294..5627dcf 100644
--- a/packages/flutter/test/widgets/text_golden_test.dart
+++ b/packages/flutter/test/widgets/text_golden_test.dart
@@ -151,15 +151,16 @@
decoration: const BoxDecoration(
color: Colors.green,
),
- child: Text.rich(
- TextSpan(
+ child: RichText(
+ textDirection: TextDirection.ltr,
+ text: TextSpan(
text: 'text1 ',
style: TextStyle(
color: translucentGreen,
background: Paint()
..color = red.withOpacity(0.5),
),
- children: <InlineSpan>[
+ children: <TextSpan>[
TextSpan(
text: 'text2',
style: TextStyle(
@@ -170,7 +171,6 @@
),
],
),
- textDirection: TextDirection.ltr,
),
),
),
@@ -242,7 +242,7 @@
find.byType(Container),
matchesGoldenFile('text_golden.StrutDefault.png'),
);
- }, skip: true); // Should only be on linux (skip: !isLinux).
+ }, skip: true); // Should only be on linux (skip: !Platform.isLinux).
// Disabled for now until font inconsistency is resolved.
testWidgets('Strut text 1', (WidgetTester tester) async {
@@ -270,7 +270,7 @@
find.byType(Container),
matchesGoldenFile('text_golden.Strut.1.1.png'),
);
- }, skip: true); // Should only be on linux (skip: !isLinux).
+ }, skip: true); // Should only be on linux (skip: !Platform.isLinux).
// Disabled for now until font inconsistency is resolved.
testWidgets('Strut text 2', (WidgetTester tester) async {
@@ -299,7 +299,7 @@
find.byType(Container),
matchesGoldenFile('text_golden.Strut.2.1.png'),
);
- }, skip: true); // Should only be on linux (skip: !isLinux).
+ }, skip: true); // Should only be on linux (skip: !Platform.isLinux).
// Disabled for now until font inconsistency is resolved.
testWidgets('Strut text rich', (WidgetTester tester) async {
@@ -319,7 +319,7 @@
color: Colors.red,
fontSize: 30,
),
- children: <InlineSpan>[
+ children: <TextSpan>[
TextSpan(
text: 'Second line!\n',
style: TextStyle(
@@ -351,7 +351,7 @@
find.byType(Container),
matchesGoldenFile('text_golden.Strut.3.1.png'),
);
- }, skip: true); // Should only be on linux (skip: !isLinux).
+ }, skip: true); // Should only be on linux (skip: !Platform.isLinux).
// Disabled for now until font inconsistency is resolved.
testWidgets('Strut text font fallback', (WidgetTester tester) async {
@@ -387,7 +387,7 @@
find.byType(Container),
matchesGoldenFile('text_golden.Strut.4.1.png'),
);
- }, skip: true); // Should only be on linux (skip: !isLinux).
+ }, skip: true); // Should only be on linux (skip: !Platform.isLinux).
// Disabled for now until font inconsistency is resolved.
testWidgets('Strut text rich forceStrutHeight', (WidgetTester tester) async {
@@ -407,7 +407,7 @@
color: Colors.red,
fontSize: 30,
),
- children: <InlineSpan>[
+ children: <TextSpan>[
TextSpan(
text: 'Second line!\n',
style: TextStyle(
@@ -439,7 +439,7 @@
find.byType(Container),
matchesGoldenFile('text_golden.StrutForce.1.1.png'),
);
- }, skip: true); // Should only be on linux (skip: !isLinux).
+ }, skip: true); // Should only be on linux (skip: !Platform.isLinux).
// Disabled for now until font inconsistency is resolved.
testWidgets('Decoration thickness', (WidgetTester tester) async {
@@ -518,807 +518,4 @@
matchesGoldenFile('text_golden.DecorationThickness.1.0.png'),
);
}, skip: !isLinux); // Coretext uses different thicknesses for decoration
-
- testWidgets('Text Inline widget', (WidgetTester tester) async {
- await tester.pumpWidget(
- Center(
- child: RepaintBoundary(
- child: Material(
- child: Directionality(
- textDirection: TextDirection.ltr,
- child: Container(
- width: 400.0,
- height: 200.0,
- decoration: const BoxDecoration(
- color: Color(0xff00ff00),
- ),
- child: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 100),
- child: const Text.rich(
- TextSpan(
- text: 'C ',
- style: TextStyle(
- fontSize: 16,
- ),
- children: <InlineSpan>[
- WidgetSpan(
- child: Checkbox(value: true, onChanged: null),
- ),
- WidgetSpan(
- child: Checkbox(value: false, onChanged: null),
- ),
- TextSpan(text: 'He ', style: TextStyle(fontSize: 20)),
- WidgetSpan(
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffffff00),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffff0000),
- ),
- )
- ),
- ),
- )
- ),
- ),
- TextSpan(text: 'hello world! sieze the day!'),
- WidgetSpan(
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- child: Checkbox(value: false, onChanged: null),
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic
- ),
- WidgetSpan(
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- child: Text('embedded'),
- ),
- ],
- ),
- textDirection: TextDirection.ltr,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- await expectLater(
- find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidget.1.1.png'),
- );
- }, skip: !isLinux); // Coretext uses different thicknesses for decoration
-
- testWidgets('Text Inline widget textfield', (WidgetTester tester) async {
- await tester.pumpWidget(
- Center(
- child: MaterialApp(
- home: RepaintBoundary(
- child: Material(
- child: Container(
- width: 400.0,
- height: 200.0,
- decoration: const BoxDecoration(
- color: Color(0xff00ff00),
- ),
- child: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 100),
- child: const Text.rich(
- TextSpan(
- text: 'My name is: ',
- style: TextStyle(
- fontSize: 20,
- ),
- children: <InlineSpan>[
- WidgetSpan(
- child: SizedBox(width: 70, height: 25, child: TextField()),
- ),
- TextSpan(text: ', and my favorite city is: ', style: TextStyle(fontSize: 20)),
- WidgetSpan(
- child: SizedBox(width: 70, height: 25, child: TextField()),
- ),
- ],
- ),
- textDirection: TextDirection.ltr,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- await expectLater(
- find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidget.2.2.png'),
- );
- }, skip: !isLinux); // Coretext uses different thicknesses for decoration
-
- // This tests if multiple Text.rich widgets are able to inline nest within each other.
- testWidgets('Text Inline widget nesting', (WidgetTester tester) async {
- await tester.pumpWidget(
- Center(
- child: MaterialApp(
- home: RepaintBoundary(
- child: Material(
- child: Container(
- width: 400.0,
- height: 200.0,
- decoration: const BoxDecoration(
- color: Color(0xff00ff00),
- ),
- child: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 100),
- child: const Text.rich(
- TextSpan(
- text: 'outer',
- style: TextStyle(
- fontSize: 20,
- ),
- children: <InlineSpan>[
- WidgetSpan(
- child: Text.rich(
- TextSpan(
- text: 'inner',
- style: TextStyle(color: Color(0xff402f4ff)),
- children: <InlineSpan>[
- WidgetSpan(
- child: Text.rich(
- TextSpan(
- text: 'inner2',
- style: TextStyle(color: Color(0xff003ffff)),
- children: <InlineSpan>[
- WidgetSpan(
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffffff30),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xff5f00f0),
- ),
- )
- ),
- ),
- )
- ),
- ),
- ],
- ),
- ),
- ),
- WidgetSpan(
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xff5fff00),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xff5f0000),
- ),
- )
- ),
- ),
- )
- ),
- ),
- ],
- ),
- ),
- ),
- TextSpan(text: 'outer', style: TextStyle(fontSize: 20)),
- WidgetSpan(
- child: SizedBox(width: 70, height: 25, child: TextField()),
- ),
- WidgetSpan(
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffff00ff),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xff0000ff),
- ),
- )
- ),
- ),
- )
- ),
- ),
- ],
- ),
- textDirection: TextDirection.ltr,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- await expectLater(
- find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetNest.1.2.png'),
- );
- }, skip: !isLinux); // Coretext uses different thicknesses for decoration
-
- testWidgets('Text Inline widget baseline', (WidgetTester tester) async {
- await tester.pumpWidget(
- Center(
- child: RepaintBoundary(
- child: Material(
- child: Directionality(
- textDirection: TextDirection.ltr,
- child: Container(
- width: 400.0,
- height: 200.0,
- decoration: const BoxDecoration(
- color: Color(0xff00ff00),
- ),
- child: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 100),
- child: const Text.rich(
- TextSpan(
- text: 'C ',
- style: TextStyle(
- fontSize: 16,
- ),
- children: <InlineSpan>[
- WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: true, onChanged: null),
- ),
- WidgetSpan(
- child: Checkbox(value: false, onChanged: null),
- ),
- TextSpan(text: 'He ', style: TextStyle(fontSize: 20)),
- WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffffff00),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffff0000),
- ),
- )
- ),
- ),
- )
- ),
- ),
- TextSpan(text: 'hello world! sieze the day!'),
- WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: Text('embedded'),
- ),
- TextSpan(text: 'ref'),
- ],
- ),
- textDirection: TextDirection.ltr,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- await expectLater(
- find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetBaseline.1.1.png'),
- );
- }, skip: !isLinux); // Coretext uses different thicknesses for decoration
-
- testWidgets('Text Inline widget aboveBaseline', (WidgetTester tester) async {
- await tester.pumpWidget(
- Center(
- child: RepaintBoundary(
- child: Material(
- child: Directionality(
- textDirection: TextDirection.ltr,
- child: Container(
- width: 400.0,
- height: 200.0,
- decoration: const BoxDecoration(
- color: Color(0xff00ff00),
- ),
- child: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 100),
- child: const Text.rich(
- TextSpan(
- text: 'C ',
- style: TextStyle(
- fontSize: 16,
- ),
- children: <InlineSpan>[
- WidgetSpan(
- alignment: PlaceholderAlignment.aboveBaseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: true, onChanged: null),
- ),
- WidgetSpan(
- child: Checkbox(value: false, onChanged: null),
- ),
- TextSpan(text: 'He ', style: TextStyle(fontSize: 20)),
- WidgetSpan(
- alignment: PlaceholderAlignment.aboveBaseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffffff00),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffff0000),
- ),
- )
- ),
- ),
- )
- ),
- ),
- TextSpan(text: 'hello world! sieze the day!'),
- WidgetSpan(
- alignment: PlaceholderAlignment.aboveBaseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.aboveBaseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.aboveBaseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.aboveBaseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.aboveBaseline,
- baseline: TextBaseline.alphabetic,
- child: Text('embedded'),
- ),
- TextSpan(text: 'ref'),
- ],
- ),
- textDirection: TextDirection.ltr,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- await expectLater(
- find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetAboveBaseline.1.1.png'),
- );
- }, skip: !isLinux); // Coretext uses different thicknesses for decoration
-
- testWidgets('Text Inline widget belowBaseline', (WidgetTester tester) async {
- await tester.pumpWidget(
- Center(
- child: RepaintBoundary(
- child: Material(
- child: Directionality(
- textDirection: TextDirection.ltr,
- child: Container(
- width: 400.0,
- height: 200.0,
- decoration: const BoxDecoration(
- color: Color(0xff00ff00),
- ),
- child: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 100),
- child: const Text.rich(
- TextSpan(
- text: 'C ',
- style: TextStyle(
- fontSize: 16,
- ),
- children: <InlineSpan>[
- WidgetSpan(
- alignment: PlaceholderAlignment.belowBaseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: true, onChanged: null),
- ),
- WidgetSpan(
- child: Checkbox(value: false, onChanged: null),
- ),
- TextSpan(text: 'He ', style: TextStyle(fontSize: 20)),
- WidgetSpan(
- alignment: PlaceholderAlignment.belowBaseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffffff00),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffff0000),
- ),
- )
- ),
- ),
- )
- ),
- ),
- TextSpan(text: 'hello world! sieze the day!'),
- WidgetSpan(
- alignment: PlaceholderAlignment.belowBaseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.belowBaseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.belowBaseline,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.belowBaseline,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.belowBaseline,
- baseline: TextBaseline.alphabetic,
- child: Text('embedded'),
- ),
- TextSpan(text: 'ref'),
- ],
- ),
- textDirection: TextDirection.ltr,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- await expectLater(
- find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetBelowBaseline.1.1.png'),
- );
- }, skip: !isLinux); // Coretext uses different thicknesses for decoration
-
- testWidgets('Text Inline widget top', (WidgetTester tester) async {
- await tester.pumpWidget(
- Center(
- child: RepaintBoundary(
- child: Material(
- child: Directionality(
- textDirection: TextDirection.ltr,
- child: Container(
- width: 400.0,
- height: 200.0,
- decoration: const BoxDecoration(
- color: Color(0xff00ff00),
- ),
- child: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 100),
- child: const Text.rich(
- TextSpan(
- text: 'C ',
- style: TextStyle(
- fontSize: 16,
- ),
- children: <InlineSpan>[
- WidgetSpan(
- alignment: PlaceholderAlignment.top,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: true, onChanged: null),
- ),
- WidgetSpan(
- child: Checkbox(value: false, onChanged: null),
- ),
- TextSpan(text: 'He ', style: TextStyle(fontSize: 20)),
- WidgetSpan(
- alignment: PlaceholderAlignment.top,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffffff00),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffff0000),
- ),
- )
- ),
- ),
- )
- ),
- ),
- TextSpan(text: 'hello world! sieze the day!'),
- WidgetSpan(
- alignment: PlaceholderAlignment.top,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.top,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.top,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.top,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.top,
- baseline: TextBaseline.alphabetic,
- child: Text('embedded'),
- ),
- TextSpan(text: 'ref'),
- ],
- ),
- textDirection: TextDirection.ltr,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- await expectLater(
- find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetTop.1.1.png'),
- );
- }, skip: !isLinux); // Coretext uses different thicknesses for decoration
-
- testWidgets('Text Inline widget middle', (WidgetTester tester) async {
- await tester.pumpWidget(
- Center(
- child: RepaintBoundary(
- child: Material(
- child: Directionality(
- textDirection: TextDirection.ltr,
- child: Container(
- width: 400.0,
- height: 200.0,
- decoration: const BoxDecoration(
- color: Color(0xff00ff00),
- ),
- child: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 100),
- child: const Text.rich(
- TextSpan(
- text: 'C ',
- style: TextStyle(
- fontSize: 16,
- ),
- children: <InlineSpan>[
- WidgetSpan(
- alignment: PlaceholderAlignment.middle,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: true, onChanged: null),
- ),
- WidgetSpan(
- child: Checkbox(value: false, onChanged: null),
- ),
- TextSpan(text: 'He ', style: TextStyle(fontSize: 20)),
- WidgetSpan(
- alignment: PlaceholderAlignment.middle,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 50.0,
- height: 55.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffffff00),
- ),
- child: Center(
- child:SizedBox(
- width: 10.0,
- height: 15.0,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color: Color(0xffff0000),
- ),
- )
- ),
- ),
- )
- ),
- ),
- TextSpan(text: 'hello world! sieze the day!'),
- WidgetSpan(
- alignment: PlaceholderAlignment.middle,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.middle,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.middle,
- baseline: TextBaseline.alphabetic,
- child: Checkbox(value: false, onChanged: null),
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.middle,
- baseline: TextBaseline.alphabetic,
- child: SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(value: true, onChanged: null),
- )
- ),
- WidgetSpan(
- alignment: PlaceholderAlignment.middle,
- baseline: TextBaseline.alphabetic,
- child: Text('embedded'),
- ),
- TextSpan(text: 'ref'),
- ],
- ),
- textDirection: TextDirection.ltr,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- await expectLater(
- find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetMiddle.1.1.png'),
- );
- }, skip: !isLinux); // Coretext uses different thicknesses for decoration
}
diff --git a/packages/flutter/test/widgets/text_test.dart b/packages/flutter/test/widgets/text_test.dart
index 269263b..48a89ba 100644
--- a/packages/flutter/test/widgets/text_test.dart
+++ b/packages/flutter/test/widgets/text_test.dart
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:flutter_test/flutter_test.dart';
-import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
-import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
import '../rendering/mock_canvas.dart';
import 'semantics_tester.dart';
@@ -294,140 +294,6 @@
semantics.dispose();
}, skip: true); // TODO(jonahwilliams): correct once https://github.com/flutter/flutter/issues/20891 is resolved.
- testWidgets('inline widgets generate semantic nodes', (WidgetTester tester) async {
- final SemanticsTester semantics = SemanticsTester(tester);
- const TextStyle textStyle = TextStyle(fontFamily: 'Ahem');
- await tester.pumpWidget(
- Text.rich(
- TextSpan(
- children: <InlineSpan>[
- const TextSpan(text: 'a '),
- TextSpan(text: 'pebble', recognizer: TapGestureRecognizer()..onTap = () { }),
- const TextSpan(text: ' in the '),
- WidgetSpan(
- child: SizedBox(
- width: 20,
- height: 40,
- child: Card(
- child: RichText(
- text: const TextSpan(text: 'INTERRUPTION'),
- textDirection: TextDirection.rtl,
- ),
- ),
- ),
- ),
- const TextSpan(text: 'sky'),
- ],
- style: textStyle,
- ),
- textDirection: TextDirection.ltr,
- ),
- );
- final TestSemantics expectedSemantics = TestSemantics.root(
- children: <TestSemantics>[
- TestSemantics.rootChild(
- children: <TestSemantics>[
- TestSemantics(
- label: 'a ',
- textDirection: TextDirection.ltr,
- ),
- TestSemantics(
- label: 'pebble',
- textDirection: TextDirection.ltr,
- actions: <SemanticsAction>[
- SemanticsAction.tap,
- ],
- ),
- TestSemantics(
- label: ' in the ',
- textDirection: TextDirection.ltr,
- ),
- TestSemantics(
- label: 'INTERRUPTION',
- textDirection: TextDirection.rtl,
- ),
- TestSemantics(
- label: 'sky',
- textDirection: TextDirection.ltr,
- ),
- ],
- ),
- ],
- );
- expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
- semantics.dispose();
- });
-
- testWidgets('inline widgets semantic nodes scale', (WidgetTester tester) async {
- final SemanticsTester semantics = SemanticsTester(tester);
- const TextStyle textStyle = TextStyle(fontFamily: 'Ahem');
- await tester.pumpWidget(
- Text.rich(
- TextSpan(
- children: <InlineSpan>[
- const TextSpan(text: 'a '),
- TextSpan(text: 'pebble', recognizer: TapGestureRecognizer()..onTap = () { }),
- const TextSpan(text: ' in the '),
- WidgetSpan(
- child: SizedBox(
- width: 20,
- height: 40,
- child: Card(
- child: RichText(
- text: const TextSpan(text: 'INTERRUPTION'),
- textDirection: TextDirection.rtl,
- ),
- ),
- ),
- ),
- const TextSpan(text: 'sky'),
- ],
- style: textStyle,
- ),
- textDirection: TextDirection.ltr,
- textScaleFactor: 2,
- ),
- );
- final TestSemantics expectedSemantics = TestSemantics.root(
- children: <TestSemantics>[
- TestSemantics.rootChild(
- rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 600.0),
- children: <TestSemantics>[
- TestSemantics(
- label: 'a ',
- textDirection: TextDirection.ltr,
- rect: const Rect.fromLTRB(-4.0, 48.0, 60.0, 84.0),
- ),
- TestSemantics(
- label: 'pebble',
- textDirection: TextDirection.ltr,
- actions: <SemanticsAction>[
- SemanticsAction.tap,
- ],
- rect: const Rect.fromLTRB(52.0, 48.0, 228.0, 84.0),
- ),
- TestSemantics(
- label: ' in the ',
- textDirection: TextDirection.ltr,
- rect: const Rect.fromLTRB(220.0, 48.0, 452.0, 84.0),
- ),
- TestSemantics(
- label: 'INTERRUPTION',
- textDirection: TextDirection.rtl,
- rect: const Rect.fromLTRB(448.0, 0.0, 488.0, 80.0),
- ),
- TestSemantics(
- label: 'sky',
- textDirection: TextDirection.ltr,
- rect: const Rect.fromLTRB(484.0, 48.0, 576.0, 84.0),
- ),
- ],
- ),
- ],
- );
- expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true,));
- semantics.dispose();
- });
testWidgets('Overflow is clipping correctly - short text with overflow: clip', (WidgetTester tester) async {
await _pumpTextWidget(
diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart
index 8db54de..046528a 100644
--- a/packages/flutter/test/widgets/widget_inspector_test.dart
+++ b/packages/flutter/test/widgets/widget_inspector_test.dart
@@ -329,10 +329,7 @@
}
// State type is private, hence using dynamic.
dynamic getInspectorState() => inspectorKey.currentState;
- String paragraphText(RenderParagraph paragraph) {
- final TextSpan textSpan = paragraph.text;
- return textSpan.text;
- }
+ String paragraphText(RenderParagraph paragraph) => paragraph.text.text;
await tester.pumpWidget(
Directionality(