Text Painting Fuzzer (#12813)
Various improvements (in particular a new painting fuzzer) to the text manual test.
Some additional documentation.
A fix to Stack to remove an LTR bias: make unpositioned children apply "alignment".
Some more debugging information on RichText and Text.
A fix to the flutter tool to not crash when an RPC call throws an exception.
diff --git a/packages/flutter/lib/src/painting/images.dart b/packages/flutter/lib/src/painting/images.dart
index b63c1e9..e02b5a3 100644
--- a/packages/flutter/lib/src/painting/images.dart
+++ b/packages/flutter/lib/src/painting/images.dart
@@ -187,6 +187,20 @@
ImageStream _imageStream;
ImageInfo _image;
+ /// Draw the image onto the given canvas.
+ ///
+ /// The image is drawn at the position and size given by the `rect` argument.
+ ///
+ /// The image is clipped to the given `clipPath`, if any.
+ ///
+ /// The `configuration` object is used to resolve the image (e.g. to pick
+ /// resolution-specific assets), and to implement the
+ /// [DecorationImage.matchTextDirection] feature.
+ ///
+ /// If the image needs to be painted again, e.g. because it is animated or
+ /// because it had not yet been loaded the first time this method was called,
+ /// then the `onChanged` callback passed to [DecorationImage.createPainter]
+ /// will be called.
void paint(Canvas canvas, Rect rect, Path clipPath, ImageConfiguration configuration) {
if (_details == null)
return;
diff --git a/packages/flutter/lib/src/rendering/list_body.dart b/packages/flutter/lib/src/rendering/list_body.dart
index cfe3cb2..76fc6dc 100644
--- a/packages/flutter/lib/src/rendering/list_body.dart
+++ b/packages/flutter/lib/src/rendering/list_body.dart
@@ -43,6 +43,10 @@
child.parentData = new ListBodyParentData();
}
+ /// The direction in which the children are laid out.
+ ///
+ /// For example, if the [axisDirection] is [AxisDirection.down], each child
+ /// will be laid out below the next, vertically.
AxisDirection get axisDirection => _axisDirection;
AxisDirection _axisDirection;
set axisDirection(AxisDirection value) {
@@ -53,6 +57,8 @@
markNeedsLayout();
}
+ /// The axis (horizontal or vertical) corresponding to the current
+ /// [axisDirection].
Axis get mainAxis => axisDirectionToAxis(axisDirection);
@override
diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart
index b0cb1a6..3bf20e5 100644
--- a/packages/flutter/lib/src/rendering/object.dart
+++ b/packages/flutter/lib/src/rendering/object.dart
@@ -2114,6 +2114,17 @@
owner.requestVisualUpdate();
}
+ /// Report the semantics of this node, for example for accessibility purposes.
+ ///
+ /// This method should be overridden by subclasses that have interesting
+ /// semantic information.
+ ///
+ /// The given [SemanticsConfiguration] object is mutable and should be
+ /// annotated in a manner that describes the current state. No reference
+ /// should be kept to that object; mutating it outside of the context of the
+ /// [describeSemanticsConfiguration] call (for example as a result of
+ /// asynchronous computation) will at best have no useful effect and at worse
+ /// will cause crashes as the data will be in an inconsistent state.
@protected
void describeSemanticsConfiguration(SemanticsConfiguration config) {
// Nothing to do by default.
diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart
index a0d7097..0fd3675 100644
--- a/packages/flutter/lib/src/rendering/paragraph.dart
+++ b/packages/flutter/lib/src/rendering/paragraph.dart
@@ -403,6 +403,20 @@
return _textPainter.getWordBoundary(position);
}
+ /// Returns the size of the text as laid out.
+ ///
+ /// This can differ from [size] if the text overflowed or if the [constraints]
+ /// provided by the parent [RenderObject] forced the layout to be bigger than
+ /// necessary for the given [text].
+ ///
+ /// This returns the [TextPainter.size] of the underlying [TextPainter].
+ ///
+ /// Valid only after [layout].
+ Size get textSize {
+ assert(!debugNeedsLayout);
+ return _textPainter.size;
+ }
+
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
diff --git a/packages/flutter/lib/src/rendering/semantics.dart b/packages/flutter/lib/src/rendering/semantics.dart
index 2ede294..e8e2bc0 100644
--- a/packages/flutter/lib/src/rendering/semantics.dart
+++ b/packages/flutter/lib/src/rendering/semantics.dart
@@ -596,9 +596,18 @@
static final SemanticsConfiguration _kEmptyConfig = new SemanticsConfiguration();
+ /// Reconfigures the properties of this object to describe the configuration
+ /// provided in the `config` argument and the children listen in the
+ /// `childrenInInversePaintOrder` argument.
+ ///
+ /// The arguments may be null; this represents an empty configuration (all
+ /// values at their defaults, no children).
+ ///
+ /// No reference is kept to the [SemanticsConfiguration] object, but the child
+ /// list is used as-is and should therefore not be changed after this call.
void updateWith({
@required SemanticsConfiguration config,
- @required List<SemanticsNode> childrenInInversePaintOrder,
+ @required List<SemanticsNode> childrenInInversePaintOrder,
}) {
config ??= _kEmptyConfig;
if (_isDifferentFromCurrentSemanticAnnotation(config))
diff --git a/packages/flutter/lib/src/rendering/stack.dart b/packages/flutter/lib/src/rendering/stack.dart
index 36b7029..0d72ef0 100644
--- a/packages/flutter/lib/src/rendering/stack.dart
+++ b/packages/flutter/lib/src/rendering/stack.dart
@@ -332,14 +332,20 @@
markNeedsLayout();
}
- /// How to align the non-positioned children in the stack.
+ /// How to align the non-positioned or partially-positioned children in the
+ /// stack.
///
/// The non-positioned children are placed relative to each other such that
/// the points determined by [alignment] are co-located. For example, if the
/// [alignment] is [Alignment.topLeft], then the top left corner of
/// each non-positioned child will be located at the same global coordinate.
///
- /// If this is set to a [AlignmentDirectional] object, then [textDirection]
+ /// Partially-positioned children, those that do not specify an alignment in a
+ /// particular axis (e.g. that have neither `top` nor `bottom` set), use the
+ /// alignment to determine how they should be positioned in that
+ /// under-specified axis.
+ ///
+ /// If this is set to an [AlignmentDirectional] object, then [textDirection]
/// must not be null.
AlignmentGeometry get alignment => _alignment;
AlignmentGeometry _alignment;
@@ -504,20 +510,26 @@
child.layout(childConstraints, parentUsesSize: true);
- double x = 0.0;
- if (childParentData.left != null)
+ double x;
+ if (childParentData.left != null) {
x = childParentData.left;
- else if (childParentData.right != null)
+ } else if (childParentData.right != null) {
x = size.width - childParentData.right - child.size.width;
+ } else {
+ x = _resolvedAlignment.alongOffset(size - child.size).dx;
+ }
if (x < 0.0 || x + child.size.width > size.width)
_hasVisualOverflow = true;
- double y = 0.0;
- if (childParentData.top != null)
+ double y;
+ if (childParentData.top != null) {
y = childParentData.top;
- else if (childParentData.bottom != null)
+ } else if (childParentData.bottom != null) {
y = size.height - childParentData.bottom - child.size.height;
+ } else {
+ y = _resolvedAlignment.alongOffset(size - child.size).dy;
+ }
if (y < 0.0 || y + child.size.height > size.height)
_hasVisualOverflow = true;
diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart
index a000c6a..90114c8 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -2239,9 +2239,10 @@
/// Positioned children are those wrapped in a [Positioned] widget that has at
/// least one non-null property. The stack sizes itself to contain all the
/// non-positioned children, which are positioned according to [alignment]
-/// (which defaults to the top-left corner). The positioned children are then
-/// placed relative to the stack according to their top, right, bottom, and left
-/// properties.
+/// (which defaults to the top-left corner in left-to-right environments and the
+/// top-right corner in right-to-left environments). The positioned children are
+/// then placed relative to the stack according to their top, right, bottom, and
+/// left properties.
///
/// The stack paints its children in order with the first child being at the
/// bottom. If you want to change the order in which the children paint, you
@@ -2282,12 +2283,18 @@
List<Widget> children: const <Widget>[],
}) : super(key: key, children: children);
- /// How to align the non-positioned children in the stack.
+ /// How to align the non-positioned and partially-positioned children in the
+ /// stack.
///
/// The non-positioned children are placed relative to each other such that
/// the points determined by [alignment] are co-located. For example, if the
/// [alignment] is [Alignment.topLeft], then the top left corner of
/// each non-positioned child will be located at the same global coordinate.
+ ///
+ /// Partially-positioned children, those that do not specify an alignment in a
+ /// particular axis (e.g. that have neither `top` nor `bottom` set), use the
+ /// alignment to determine how they should be positioned in that
+ /// under-specified axis.
final AlignmentGeometry alignment;
/// The text direction with which to resolve [alignment].
@@ -2398,6 +2405,12 @@
/// [height] properties can be used to give the dimensions, with one
/// corresponding position property (e.g. [top] and [height]).
///
+/// If all three values on a particular axis are null, then the
+/// [Stack.alignment] property is used to position the child.
+///
+/// If all six values are null, the child is a non-positioned child. The [Stack]
+/// uses only the non-positioned children to size itself.
+///
/// See also:
///
/// * [PositionedDirectional], which adapts to the ambient [Directionality].
@@ -2413,6 +2426,8 @@
///
/// * [Positioned.directional], which specifies the widget's horizontal
/// position using `start` and `end` rather than `left` and `right`.
+ /// * [PositionedDirectional], which is similar to [Positioned.directional]
+ /// but adapts to the ambient [Directionality].
const Positioned({
Key key,
this.left,
@@ -2530,36 +2545,54 @@
///
/// Only two out of the three horizontal values ([left], [right], [width]) can be
/// set. The third must be null.
+ ///
+ /// If all three are null, the [Stack.alignment] is used to position the child
+ /// horizontally.
final double left;
/// The distance that the child's top edge is inset from the top of the stack.
///
/// Only two out of the three vertical values ([top], [bottom], [height]) can be
/// set. The third must be null.
+ ///
+ /// If all three are null, the [Stack.alignment] is used to position the child
+ /// vertically.
final double top;
/// The distance that the child's right edge is inset from the right of the stack.
///
/// Only two out of the three horizontal values ([left], [right], [width]) can be
/// set. The third must be null.
+ ///
+ /// If all three are null, the [Stack.alignment] is used to position the child
+ /// horizontally.
final double right;
/// The distance that the child's bottom edge is inset from the bottom of the stack.
///
/// Only two out of the three vertical values ([top], [bottom], [height]) can be
/// set. The third must be null.
+ ///
+ /// If all three are null, the [Stack.alignment] is used to position the child
+ /// vertically.
final double bottom;
/// The child's width.
///
/// Only two out of the three horizontal values ([left], [right], [width]) can be
/// set. The third must be null.
+ ///
+ /// If all three are null, the [Stack.alignment] is used to position the child
+ /// horizontally.
final double width;
/// The child's height.
///
/// Only two out of the three vertical values ([top], [bottom], [height]) can be
/// set. The third must be null.
+ ///
+ /// If all three are null, the [Stack.alignment] is used to position the child
+ /// vertically.
final double height;
@override
@@ -3897,6 +3930,18 @@
..textScaleFactor = textScaleFactor
..maxLines = maxLines;
}
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder description) {
+ super.debugFillProperties(description);
+ description.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: TextAlign.start));
+ description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
+ description.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
+ description.add(new EnumProperty<TextOverflow>('overflow', overflow, defaultValue: TextOverflow.clip));
+ description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: 1.0));
+ description.add(new IntProperty('maxLines', maxLines, ifNull: 'unlimited'));
+ description.add(new StringProperty('text', text.toPlainText()));
+ }
}
/// A widget that displays a [dart:ui.Image] directly.
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index c047394..04be6d5 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -4519,6 +4519,11 @@
void removeChildRenderObject(RenderObject child) {
assert(false);
}
+
+ @override
+ List<DiagnosticsNode> debugDescribeChildren() {
+ return widget.debugDescribeChildren();
+ }
}
/// An [Element] that uses a [SingleChildRenderObjectWidget] as its configuration.
diff --git a/packages/flutter/lib/src/widgets/icon_data.dart b/packages/flutter/lib/src/widgets/icon_data.dart
index 98c4a4d..c34a754 100644
--- a/packages/flutter/lib/src/widgets/icon_data.dart
+++ b/packages/flutter/lib/src/widgets/icon_data.dart
@@ -29,7 +29,14 @@
/// The font family from which the glyph for the [codePoint] will be selected.
final String fontFamily;
- // The name of the package from which the font family is included.
+ /// The name of the package from which the font family is included.
+ ///
+ /// The name is used by the [Icon] widget when configuring the [TextStyle] so
+ /// that the given [fontFamily] is obtained from the appropriate asset.
+ ///
+ /// See also:
+ ///
+ /// * [TextStyle], which describes how to use fonts from other packages.
final String fontPackage;
@override
diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart
index b8165f7..a2ff2df 100644
--- a/packages/flutter/lib/src/widgets/text.dart
+++ b/packages/flutter/lib/src/widgets/text.dart
@@ -292,5 +292,11 @@
super.debugFillProperties(description);
description.add(new StringProperty('data', data, showName: false));
style?.debugFillProperties(description);
+ description.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
+ description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
+ description.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
+ description.add(new EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
+ description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
+ description.add(new IntProperty('maxLines', maxLines, defaultValue: null));
}
}
diff --git a/packages/flutter/test/painting/text_painter_rtl_test.dart b/packages/flutter/test/painting/text_painter_rtl_test.dart
index e1edb61..065d7ac 100644
--- a/packages/flutter/test/painting/text_painter_rtl_test.dart
+++ b/packages/flutter/test/painting/text_painter_rtl_test.dart
@@ -46,6 +46,9 @@
expect(painter.text.text.length, 28);
painter.layout();
+ // The skips here are because the old rendering code considers the bidi formatting characters
+ // to be part of the word sometimes and not others, which is fine, but we'd mildly prefer if
+ // we were consistently considering them part of words always.
final TextRange hebrew1 = painter.getWordBoundary(const TextPosition(offset: 4, affinity: TextAffinity.downstream));
expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs);
final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14, affinity: TextAffinity.downstream));
diff --git a/packages/flutter/test/widgets/stack_test.dart b/packages/flutter/test/widgets/stack_test.dart
index 62652e4..a3e4cdf 100644
--- a/packages/flutter/test/widgets/stack_test.dart
+++ b/packages/flutter/test/widgets/stack_test.dart
@@ -629,4 +629,122 @@
),
);
});
+
+ testWidgets('Alignment with partially-positioned children', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ new Directionality(
+ textDirection: TextDirection.rtl,
+ child: new Stack(
+ alignment: Alignment.center,
+ children: <Widget>[
+ const SizedBox(width: 100.0, height: 100.0),
+ const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ ],
+ ),
+ ),
+ );
+ expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(350.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(350.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(0.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(350.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
+
+ await tester.pumpWidget(
+ new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new Stack(
+ alignment: Alignment.center,
+ children: <Widget>[
+ const SizedBox(width: 100.0, height: 100.0),
+ const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ ],
+ ),
+ ),
+ );
+ expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(350.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(350.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(350.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
+
+ await tester.pumpWidget(
+ new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new Stack(
+ alignment: Alignment.bottomRight,
+ children: <Widget>[
+ const SizedBox(width: 100.0, height: 100.0),
+ const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ ],
+ ),
+ ),
+ );
+ expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(700.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(700.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+
+ await tester.pumpWidget(
+ new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new Stack(
+ alignment: Alignment.topLeft,
+ children: <Widget>[
+ const SizedBox(width: 100.0, height: 100.0),
+ const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+ ],
+ ),
+ ),
+ );
+ expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(0.0, 500.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
+ expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(0.0, 500.0, 100.0, 100.0));
+ });
}
diff --git a/packages/flutter/test/widgets/table_test.dart b/packages/flutter/test/widgets/table_test.dart
index 4d8c8a9..5ce9a7f 100644
--- a/packages/flutter/test/widgets/table_test.dart
+++ b/packages/flutter/test/widgets/table_test.dart
@@ -765,23 +765,23 @@
equalsIgnoringHashCodes(
'Table-[GlobalKey#00000](renderObject: RenderTable#00000)\n'
'├Text("A")\n'
- '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+ '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "A", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("B")\n'
- '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+ '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "B", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("C")\n'
- '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+ '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "C", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("D")\n'
- '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+ '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "D", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("EEE")\n'
- '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+ '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "EEE", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("F")\n'
- '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+ '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "F", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("G")\n'
- '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+ '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "G", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("H")\n'
- '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+ '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "H", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'└Text("III")\n'
- ' └RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n',
+ ' └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "III", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
),
);
});
diff --git a/packages/flutter_test/test/widget_tester_test.dart b/packages/flutter_test/test/widget_tester_test.dart
index c9970d3..af4903b 100644
--- a/packages/flutter_test/test/widget_tester_test.dart
+++ b/packages/flutter_test/test/widget_tester_test.dart
@@ -52,7 +52,7 @@
final String message = failure.message;
expect(message, contains('Expected: no matching nodes in the widget tree\n'));
- expect(message, contains('Actual: ?:<exactly one widget with text "foo": Text("foo")>\n'));
+ expect(message, contains('Actual: ?:<exactly one widget with text "foo": Text("foo", textDirection: ltr)>\n'));
expect(message, contains('Which: means one was found but none were expected\n'));
});
@@ -70,7 +70,7 @@
final String message = failure.message;
expect(message, contains('Expected: no matching nodes in the widget tree\n'));
- expect(message, contains('Actual: ?:<exactly one widget with text "foo" (ignoring offstage widgets): Text("foo")>\n'));
+ expect(message, contains('Actual: ?:<exactly one widget with text "foo" (ignoring offstage widgets): Text("foo", textDirection: ltr)>\n'));
expect(message, contains('Which: means one was found but none were expected\n'));
});
diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart
index 651c65a..7cfab01 100644
--- a/packages/flutter_tools/lib/src/vmservice.dart
+++ b/packages/flutter_tools/lib/src/vmservice.dart
@@ -738,6 +738,10 @@
} on WebSocketChannelException catch (error) {
throwToolExit('Error connecting to observatory: $error');
return null;
+ } on rpc.RpcException catch (error) {
+ printError('Error ${error.code} received from application: ${error.message}');
+ printTrace('${error.data}');
+ return null;
}
}