Unverified Commit 3541ad0a authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Add an UnconstrainedBox and factor out debug overflow indicator. (#12856)

UnconstrainedBox will allow its child to size itself as if it had no constraints, and then attempt to fit around that object, until its own constraints are exceeded, in which case it will clip and display an overflow warning.

I also factored out DebugOverflowIndicator, which will draw overflow indicators on containers which overflow but aren't expected to.
parent fb86a033
......@@ -36,6 +36,7 @@ export 'src/rendering/binding.dart';
export 'src/rendering/box.dart';
export 'src/rendering/custom_layout.dart';
export 'src/rendering/debug.dart';
export 'src/rendering/debug_overflow_indicator.dart';
export 'src/rendering/editable.dart';
export 'src/rendering/error.dart';
export 'src/rendering/flex.dart';
......
......@@ -23,6 +23,7 @@ typedef void ValueChanged<T>(T value);
/// value, regardless of whether the given value is new or not.
///
/// See also:
///
/// * [ValueGetter], the getter equivalent of this signature.
/// * [AsyncValueSetter], an asynchronous version of this signature.
typedef void ValueSetter<T>(T value);
......@@ -30,6 +31,7 @@ typedef void ValueSetter<T>(T value);
/// Signature for callbacks that are to report a value on demand.
///
/// See also:
///
/// * [ValueSetter], the setter equivalent of this signature.
/// * [AsyncValueGetter], an asynchronous version of this signature.
typedef T ValueGetter<T>();
......@@ -41,6 +43,7 @@ typedef Iterable<T> IterableFilter<T>(Iterable<T> input);
/// return a [Future] to indicate when their work is complete.
///
/// See also:
///
/// * [VoidCallback], a synchronous version of this signature.
/// * [AsyncValueGetter], a signature for asynchronous getters.
/// * [AsyncValueSetter], a signature for asynchronous setters.
......@@ -50,6 +53,7 @@ typedef Future<Null> AsyncCallback();
/// [Future] that completes when the value has been saved.
///
/// See also:
///
/// * [ValueSetter], a synchronous version of this signature.
/// * [AsyncValueGetter], the getter equivalent of this signature.
typedef Future<Null> AsyncValueSetter<T>(T value);
......@@ -57,6 +61,7 @@ typedef Future<Null> AsyncValueSetter<T>(T value);
/// Signature for callbacks that are to asynchronously report a value on demand.
///
/// See also:
///
/// * [ValueGetter], a synchronous version of this signature.
/// * [AsyncValueSetter], the setter equivalent of this signature.
typedef Future<T> AsyncValueGetter<T>();
......
......@@ -232,6 +232,7 @@ class TextField extends StatefulWidget {
/// characters.
///
/// See also:
///
/// * [LengthLimitingTextInputFormatter] for more information on how it
/// counts characters, and how it may differ from the intuitive meaning.
final int maxLength;
......
// Copyright 2017 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:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/painting.dart';
import 'object.dart';
import 'stack.dart';
// Describes which side the region data overflows on.
enum _OverflowSide {
left,
top,
bottom,
right,
}
// Data used by the DebugOverflowIndicator to manage the regions and labels for
// the indicators.
class _OverflowRegionData {
const _OverflowRegionData({
this.rect,
this.label: '',
this.labelOffset: Offset.zero,
this.rotation: 0.0,
this.side,
});
final Rect rect;
final String label;
final Offset labelOffset;
final double rotation;
final _OverflowSide side;
}
/// An mixin indicator that is drawn when a [RenderObject] overflows its
/// container.
///
/// This is used by some RenderObjects that are containers to show where, and by
/// how much, their children overflow their containers. These indicators are
/// typically only shown in a debug build (where the call to
/// [paintOverflowIndicator] is surrounded by an assert).
///
/// This class will also print a debug message to the console when the container
/// overflows. It will print on the first occurrence, and once after each time that
/// [reassemble] is called.
///
/// ## Sample code
///
/// ```dart
/// class MyRenderObject extends RenderAligningShiftedBox with DebugOverflowIndicatorMixin {
/// MyRenderObject({
/// AlignmentGeometry alignment,
/// TextDirection textDirection,
/// RenderBox child,
/// }) : super.mixin(alignment, textDirection, child);
///
/// Rect _containerRect;
/// Rect _childRect;
///
/// @override
/// void performLayout() {
/// // ...
/// final BoxParentData childParentData = child.parentData;
/// _containerRect = Offset.zero & size;
/// _childRect = childParentData.offset & child.size;
/// }
///
/// @override
/// void paint(PaintingContext context, Offset offset) {
/// // Do normal painting here...
/// // ...
///
/// assert(() {
/// paintOverflowIndicator(context, offset, _containerRect, _childRect);
/// return true;
/// }());
/// }
/// }
/// ```
///
/// See also:
///
/// * The code for [RenderUnconstrainedBox] and [RenderFlex] for examples of
/// classes that use this indicator mixin.
abstract class DebugOverflowIndicatorMixin extends RenderObject {
// This class is intended to be used as a mixin, and should not be
// extended directly.
factory DebugOverflowIndicatorMixin._() => null;
static const Color _black = const Color(0xBF000000);
static const Color _yellow = const Color(0xBFFFFF00);
// The fraction of the container that the indicator covers.
static const double _indicatorFraction = 0.1;
static const double _indicatorFontSizePixels = 7.5;
static const double _indicatorLabelPaddingPixels = 1.0;
static const TextStyle _indicatorTextStyle = const TextStyle(
color: const Color(0xFF900000),
fontSize: _indicatorFontSizePixels,
fontWeight: FontWeight.w800,
);
static final Paint _indicatorPaint = new Paint()
..shader = new ui.Gradient.linear(
const Offset(0.0, 0.0),
const Offset(10.0, 10.0),
<Color>[_black, _yellow, _yellow, _black],
<double>[0.25, 0.25, 0.75, 0.75],
TileMode.repeated,
);
static final Paint _labelBackgroundPaint = new Paint()..color = const Color(0xFFFFFFFF);
final List<TextPainter> _indicatorLabel = new List<TextPainter>.filled(
_OverflowSide.values.length,
new TextPainter(textDirection: TextDirection.ltr), // This label is in English.
);
// Set to true to trigger a debug message in the console upon
// the next paint call. Will be reset after each paint.
bool _overflowReportNeeded = true;
String _formatPixels(double value) {
assert(value > 0.0);
String pixels;
if (value > 10.0) {
pixels = value.toStringAsFixed(0);
} else if (value > 1.0) {
pixels = value.toStringAsFixed(1);
} else {
pixels = value.toStringAsPrecision(3);
}
return pixels;
}
List<_OverflowRegionData> _calculateOverflowRegions(RelativeRect overflow, Rect containerRect) {
final List<_OverflowRegionData> regions = <_OverflowRegionData>[];
if (overflow.left > 0.0) {
final Rect markerRect = new Rect.fromLTWH(
0.0,
0.0,
containerRect.width * _indicatorFraction,
containerRect.height,
);
regions.add(new _OverflowRegionData(
rect: markerRect,
label: 'LEFT OVERFLOWED BY ${_formatPixels(overflow.left)} PIXELS',
labelOffset: markerRect.centerLeft +
const Offset(_indicatorFontSizePixels + _indicatorLabelPaddingPixels, 0.0),
rotation: math.PI / 2.0,
side: _OverflowSide.left,
));
}
if (overflow.right > 0.0) {
final Rect markerRect = new Rect.fromLTWH(
containerRect.width * (1.0 - _indicatorFraction),
0.0,
containerRect.width * _indicatorFraction,
containerRect.height,
);
regions.add(new _OverflowRegionData(
rect: markerRect,
label: 'RIGHT OVERFLOWED BY ${_formatPixels(overflow.right)} PIXELS',
labelOffset: markerRect.centerRight -
const Offset(_indicatorFontSizePixels + _indicatorLabelPaddingPixels, 0.0),
rotation: -math.PI / 2.0,
side: _OverflowSide.right,
));
}
if (overflow.top > 0.0) {
final Rect markerRect = new Rect.fromLTWH(
0.0,
0.0,
containerRect.width,
containerRect.height * _indicatorFraction,
);
regions.add(new _OverflowRegionData(
rect: markerRect,
label: 'TOP OVERFLOWED BY ${_formatPixels(overflow.top)} PIXELS',
labelOffset: markerRect.topCenter + const Offset(0.0, _indicatorLabelPaddingPixels),
rotation: 0.0,
side: _OverflowSide.top,
));
}
if (overflow.bottom > 0.0) {
final Rect markerRect = new Rect.fromLTWH(
0.0,
containerRect.height * (1.0 - _indicatorFraction),
containerRect.width,
containerRect.height * _indicatorFraction,
);
regions.add(new _OverflowRegionData(
rect: markerRect,
label: 'BOTTOM OVERFLOWED BY ${_formatPixels(overflow.bottom)} PIXELS',
labelOffset: markerRect.bottomCenter -
const Offset(0.0, _indicatorFontSizePixels + _indicatorLabelPaddingPixels),
rotation: 0.0,
side: _OverflowSide.bottom,
));
}
return regions;
}
void _reportOverflow(RelativeRect overflow, String overflowHints) {
overflowHints ??= 'The edge of the $runtimeType that is '
'overflowing has been marked in the rendering with a yellow and black '
'striped pattern. This is usually caused by the contents being too big '
'for the $runtimeType.\n'
'This is considered an error condition because it indicates that there '
'is content that cannot be seen. If the content is legitimately bigger '
'than the available space, consider clipping it with a ClipRect widget '
'before putting it in the $runtimeType, or using a scrollable '
'container, like a ListView.';
final List<String> overflows = <String>[];
if (overflow.left > 0.0)
overflows.add('${_formatPixels(overflow.left)} pixels on the left');
if (overflow.top > 0.0)
overflows.add('${_formatPixels(overflow.top)} pixels on the top');
if (overflow.bottom > 0.0)
overflows.add('${_formatPixels(overflow.bottom)} pixels on the bottom');
if (overflow.right > 0.0)
overflows.add('${_formatPixels(overflow.right)} pixels on the right');
String overflowText = '';
assert(overflows.isNotEmpty,
"Somehow $runtimeType didn't actually overflow like it thought it did.");
switch (overflows.length) {
case 1:
overflowText = overflows.first;
break;
case 2:
overflowText = '${overflows.first} and ${overflows.last}';
break;
default:
overflows[overflows.length - 1] = 'and ${overflows[overflows.length - 1]}';
overflowText = overflows.join(', ');
}
FlutterError.reportError(
new FlutterErrorDetailsForRendering(
exception: 'A $runtimeType overflowed by $overflowText.',
library: 'rendering library',
context: 'during layout',
renderObject: this,
informationCollector: (StringBuffer information) {
information.writeln(overflowHints);
information.writeln('The specific $runtimeType in question is:');
information.writeln(' ${toStringShallow(joiner: '\n ')}');
information.writeln('◢◤' * (FlutterError.wrapWidth ~/ 2));
},
),
);
}
/// To be called when the overflow indicators should be painted.
///
/// Typically only called if there is an overflow, and only from within a
/// debug build.
///
/// See example code in [DebugOverflowIndicatorMixin] documentation.
void paintOverflowIndicator(
PaintingContext context,
Offset offset,
Rect containerRect,
Rect childRect, {
String overflowHints,
}) {
final RelativeRect overflow = new RelativeRect.fromRect(containerRect, childRect);
if (overflow.left <= 0.0 &&
overflow.right <= 0.0 &&
overflow.top <= 0.0 &&
overflow.bottom <= 0.0) {
return;
}
final List<_OverflowRegionData> overflowRegions = _calculateOverflowRegions(overflow, containerRect);
for (_OverflowRegionData region in overflowRegions) {
context.canvas.drawRect(region.rect.shift(offset), _indicatorPaint);
if (_indicatorLabel[region.side.index].text?.text != region.label) {
_indicatorLabel[region.side.index].text = new TextSpan(
text: region.label,
style: _indicatorTextStyle,
);
_indicatorLabel[region.side.index].layout();
}
final Offset labelOffset = region.labelOffset + offset;
final Offset centerOffset = new Offset(-_indicatorLabel[region.side.index].width / 2.0, 0.0);
final Rect textBackgroundRect = centerOffset & _indicatorLabel[region.side.index].size;
context.canvas.save();
context.canvas.translate(labelOffset.dx, labelOffset.dy);
context.canvas.rotate(region.rotation);
context.canvas.drawRect(textBackgroundRect, _labelBackgroundPaint);
_indicatorLabel[region.side.index].paint(context.canvas, centerOffset);
context.canvas.restore();
}
if (_overflowReportNeeded) {
_overflowReportNeeded = false;
_reportOverflow(overflow, overflowHints);
}
}
@override
void reassemble() {
super.reassemble();
// Users expect error messages to be shown again after hot reload.
assert(() {
_overflowReportNeeded = true;
return true;
}());
}
}
......@@ -3,11 +3,11 @@
// found in the LICENSE file.
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'box.dart';
import 'debug_overflow_indicator.dart';
import 'object.dart';
/// How the child is inscribed into the available space.
......@@ -262,7 +262,8 @@ typedef double _ChildSizingFunction(RenderBox child, double extent);
/// * [Flex], the widget equivalent.
/// * [Row] and [Column], direction-specific variants of [Flex].
class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, FlexParentData> {
RenderBoxContainerDefaultsMixin<RenderBox, FlexParentData>,
DebugOverflowIndicatorMixin {
/// Creates a flex render object.
///
/// By default, the flex layout is horizontal and children are aligned to the
......@@ -467,7 +468,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
return true;
}
/// Set during layout if overflow occurred on the main axis.
// Set during layout if overflow occurred on the main axis.
double _overflow;
@override
......@@ -924,28 +925,6 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
return defaultHitTestChildren(result, position: position);
}
static const Color _black = const Color(0xBF000000);
static const Color _yellow = const Color(0xBFFFFF00);
static const double _kMarkerSize = 0.1;
static const TextStyle _debugMarkerTextStyle = const TextStyle(
color: const Color(0xFF900000),
fontSize: 7.5,
fontWeight: FontWeight.w800,
);
static Paint _debugMarkerPaint;
TextPainter _debugMarkerLabel;
bool _debugReportOverflow = true;
@override
void reassemble() {
super.reassemble();
assert(() {
_debugReportOverflow = true;
return true;
}());
}
@override
void paint(PaintingContext context, Offset offset) {
if (_overflow <= 0.0) {
......@@ -961,111 +940,35 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint);
assert(() {
// In debug mode, if you have overflow, we highlight where the overflow
// would be by painting the edge of that area with a yellow and black
// striped bar.
_debugMarkerPaint ??= new Paint()
..shader = new ui.Gradient.linear(
const Offset(0.0, 0.0),
const Offset(10.0, 10.0),
<Color>[_black, _yellow, _yellow, _black],
<double>[0.25, 0.25, 0.75, 0.75],
TileMode.repeated,
);
String pixels;
if (_overflow > 10.0) {
pixels = _overflow.toStringAsFixed(0);
} else if (_overflow > 1.0) {
pixels = _overflow.toStringAsFixed(1);
} else {
pixels = _overflow.toStringAsPrecision(3);
}
Rect markerRect;
String label;
Offset labelOffset;
double labelAngle;
switch (direction) {
case Axis.horizontal:
if (textDirection != null) {
final Size markerSize = new Size(size.width * _kMarkerSize, size.height);
switch (textDirection) {
case TextDirection.rtl:
labelAngle = math.PI / 2.0;
markerRect = offset + new Offset(-size.width * _kMarkerSize, 0.0) & markerSize;
labelOffset = markerRect.centerLeft;
break;
case TextDirection.ltr:
labelAngle = -math.PI / 2.0;
markerRect = offset + new Offset(size.width * (1.0 - _kMarkerSize), 0.0) & markerSize;
labelOffset = markerRect.centerRight;
break;
}
} else {
markerRect = (offset & size).deflate(size.shortestSide * _kMarkerSize);
labelOffset = markerRect.center;
labelAngle = 0.0;
}
label = 'ROW OVERFLOWED BY $pixels PIXELS';
break;
case Axis.vertical:
markerRect = offset + new Offset(0.0, size.height * (1.0 - _kMarkerSize)) &
new Size(size.width, size.height * _kMarkerSize);
label = 'COLUMN OVERFLOWED BY $pixels PIXELS';
break;
}
context.canvas.drawRect(markerRect, _debugMarkerPaint);
_debugMarkerLabel ??= new TextPainter()
..textDirection = TextDirection.ltr; // This label is in English.
_debugMarkerLabel.text = new TextSpan( // This is a no-op if the label hasn't changed.
text: label,
style: _debugMarkerTextStyle,
);
_debugMarkerLabel.layout(); // This is a no-op if the label hasn't changed.
switch (direction) {
// Only set this if it's null to save work. It gets reset to null if the
// _direction changes.
final String debugOverflowHints =
'The overflowing $runtimeType has an orientation of $_direction.\n'
'The edge of the $runtimeType that is overflowing has been marked '
'in the rendering with a yellow and black striped pattern. This is '
'usually caused by the contents being too big for the $runtimeType. '
'Consider applying a flex factor (e.g. using an Expanded widget) to '
'force the children of the $runtimeType to fit within the available '
'space instead of being sized to their natural size.\n'
'This is considered an error condition because it indicates that there '
'is content that cannot be seen. If the content is legitimately bigger '
'than the available space, consider clipping it with a ClipRect widget '
'before putting it in the flex, or using a scrollable container rather '
'than a Flex, like a ListView.';
// Simulate a child rect that overflows by the right amount. This child
// rect is never used for drawing, just for determining the overflow
// location and amount.
Rect overflowChildRect;
switch (_direction) {
case Axis.horizontal:
context.canvas.save();
context.canvas.translate(labelOffset.dx, labelOffset.dy);
context.canvas.rotate(labelAngle);
_debugMarkerLabel.paint(context.canvas, new Offset(-_debugMarkerLabel.width / 2.0, 0.0));
context.canvas.restore();
overflowChildRect = new Rect.fromLTWH(0.0, 0.0, size.width + _overflow, 0.0);
break;
case Axis.vertical:
_debugMarkerLabel.paint(context.canvas, markerRect.bottomCenter - new Offset(_debugMarkerLabel.width / 2.0, 0.0));
overflowChildRect = new Rect.fromLTWH(0.0, 0.0, 0.0, size.height + _overflow);
break;
}
if (_debugReportOverflow) {
_debugReportOverflow = false;
FlutterError.reportError(new FlutterErrorDetailsForRendering(
exception: 'A ${describeEnum(direction)} $runtimeType overflowed by $pixels pixels.',
library: 'rendering library',
context: 'during layout',
renderObject: this,
informationCollector: (StringBuffer information) {
information.writeln(
'The edge of the $runtimeType that is overflowing has been marked in the rendering '
'with a yellow and black striped pattern. This is usually caused by the contents '
'being too big for the $runtimeType. Consider applying a flex factor (e.g. using '
'an Expanded widget) to force the children of the $runtimeType to fit within the '
'available space instead of being sized to their natural size.'
);
information.writeln(
'This is considered an error condition because it indicates that there is content '
'that cannot be seen. If the content is legitimately bigger than the available '
'space, consider clipping it with a ClipRect widget before putting it in the flex, '
'or using a scrollable container rather than a Flex, for example using ListView.'
);
information.writeln('The specific $runtimeType in question is:');
information.writeln(' ${toStringShallow(joiner: '\n ')}');
information.writeln('◢◤' * (FlutterError.wrapWidth ~/ 2));
}
));
}
paintOverflowIndicator(context, offset, Offset.zero & size, overflowChildRect, overflowHints: debugOverflowHints);
return true;
}());
}
......@@ -1092,5 +995,4 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
description.add(new EnumProperty<VerticalDirection>('verticalDirection', verticalDirection, defaultValue: null));
description.add(new EnumProperty<TextBaseline>('textBaseline', textBaseline, defaultValue: null));
}
}
......@@ -2970,6 +2970,7 @@ abstract class _SemanticsFragment {
/// previously painted [RenderObject]s unreachable for accessibility purposes.
///
/// See also:
///
/// * [SemanticsConfiguration.isBlockingSemanticsOfPreviouslyPaintedNodes]
/// describes what semantics are dropped in more detail.
final bool dropsSemanticsOfPreviousSiblings;
......
......@@ -27,7 +27,7 @@ export 'package:flutter/gestures.dart' show
///
/// A proxy box has a single child and simply mimics all the properties of that
/// child by calling through to the child for each function in the render box
/// protocol. For example, a proxy box determines its size by askings its child
/// protocol. For example, a proxy box determines its size by asking its child
/// to layout with the same constraints and then matching the size.
///
/// A proxy box isn't useful on its own because you might as well just replace
......
......@@ -8,7 +8,9 @@ import 'package:flutter/foundation.dart';
import 'box.dart';
import 'debug.dart';
import 'debug_overflow_indicator.dart';
import 'object.dart';
import 'stack.dart' show RelativeRect;
/// Abstract class for one-child-layout render boxes that provide control over
/// the child's position.
......@@ -236,6 +238,12 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
_textDirection = textDirection,
super(child);
/// A constructor to be used only when the extending class also has a mixin.
// TODO(gspencer): Remove this constructor once https://github.com/dart-lang/sdk/issues/15101 is fixed.
@protected
RenderAligningShiftedBox.mixin(AlignmentGeometry alignment,TextDirection textDirection, RenderBox child)
: this(alignment: alignment, textDirection: textDirection, child: child);
Alignment _resolvedAlignment;
void _resolve() {
......@@ -467,6 +475,16 @@ class RenderPositionedBox extends RenderAligningShiftedBox {
/// The child is positioned according to [alignment]. To position a smaller
/// child inside a larger parent, use [RenderPositionedBox] and
/// [RenderConstrainedBox] rather than RenderConstrainedOverflowBox.
///
/// See also:
///
/// * [RenderUnconstrainedBox] for a render object that allows its children
/// to render themselves unconstrained, expands to fit them, and considers
/// overflow to be an error.
/// * [RenderSizedOverflowBox], a render object that is a specific size but
/// passes its original constraints through to its child, which it allows to
/// overflow.
class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
/// Creates a render object that lets its child overflow itself.
RenderConstrainedOverflowBox({
......@@ -562,8 +580,110 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
}
}
/// A render box that is a specific size but passes its original constraints
/// through to its child, which will probably overflow.
/// Renders a box, imposing no constraints on its child, allowing the child to
/// render at its "natural" size.
///
/// This allows a child to render at the size it would render if it were alone
/// on an infinite canvas with no constraints. This box will then expand
/// as much as it can within its own constraints and align the child based on
/// [alignment]. If the box cannot expand enough to accommodate the entire
/// child, the child will be clipped.
///
/// In debug mode, if the child overflows the box, a warning will be printed on
/// the console, and black and yellow striped areas will appear where theR
/// overflow occurs.
///
/// See also:
///
/// * [RenderConstrainedBox] renders a box which imposes constraints on its
/// child.
/// * [RenderConstrainedOverflowBox], renders a box that imposes different
/// constraints on its child than it gets from its parent, possibly allowing
/// the child to overflow the parent.
/// * [RenderSizedOverflowBox], a render object that is a specific size but
/// passes its original constraints through to its child, which it allows to
/// overflow.
class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflowIndicatorMixin {
RenderUnconstrainedBox({
@required AlignmentGeometry alignment,
@required TextDirection textDirection,
RenderBox child,
}) : assert(alignment != null),
super.mixin(alignment, textDirection, child);
Rect _overflowContainerRect = Rect.zero;
Rect _overflowChildRect = Rect.zero;
bool _isOverflowing = false;
@override
void performLayout() {
if (child != null) {
// Let the child lay itself out at it's "natural" size.
child.layout(const BoxConstraints(), parentUsesSize: true);
size = constraints.constrain(child.size);
alignChild();
final BoxParentData childParentData = child.parentData;
_overflowContainerRect = Offset.zero & size;
_overflowChildRect = childParentData.offset & child.size;
} else {
size = constraints.constrain(Size.zero);
_overflowContainerRect = Rect.zero;
_overflowChildRect = Rect.zero;
}
final RelativeRect overflow = new RelativeRect.fromRect(_overflowContainerRect, _overflowChildRect);
_isOverflowing = overflow.left > 0.0 ||
overflow.right > 0.0 ||
overflow.top > 0.0 ||
overflow.bottom > 0.0;
}
@override
void paint(PaintingContext context, Offset offset) {
// There's no point in drawing the child if we're empty, or there is no
// child.
if (child == null || size.isEmpty)
return;
if (!_isOverflowing) {
super.paint(context, offset);
return;
}
// We have overflow. Clip it.
context.pushClipRect(needsCompositing, offset, Offset.zero & size, super.paint);
// Display the overflow indicator.
assert(() {
paintOverflowIndicator(context, offset, _overflowContainerRect, _overflowChildRect);
return true;
}());
}
@override
Rect describeApproximatePaintClip(RenderObject child) {
return _isOverflowing ? Offset.zero & size : null;
}
@override
String toStringShort() {
String header = super.toStringShort();
if (_isOverflowing)
header += ' OVERFLOWING';
return header;
}
}
/// A render object that is a specific size but passes its original constraints
/// through to its child, which it allows to overflow.
///
/// See also:
/// * [RenderUnconstrainedBox] for a render object that allows its children
/// to render themselves unconstrained, expands to fit them, and considers
/// overflow to be an error.
/// * [RenderConstrainedOverflowBox] for a render object that imposes
/// different constraints on its child than it gets from its parent,
/// possibly allowing the child to overflow the parent.
class RenderSizedOverflowBox extends RenderAligningShiftedBox {
/// Creates a render box of a given size that lets its child overflow.
///
......
......@@ -17,12 +17,14 @@ import 'object.dart';
/// width or height of the rectangle, convert it to a [Rect] using [toRect()]
/// (passing the container's own Rect), and then examine that object.
///
/// If you create the RelativeRect with null values, the methods on
/// RelativeRect will not work usefully (or at all).
/// The fields [left], [right], [bottom], and [top] must not be null.
@immutable
class RelativeRect {
/// Creates a RelativeRect with the given values.
const RelativeRect.fromLTRB(this.left, this.top, this.right, this.bottom);
///
/// The arguments must not be null.
const RelativeRect.fromLTRB(this.left, this.top, this.right, this.bottom)
: assert(left != null && top != null && right != null && bottom != null);
/// Creates a RelativeRect from a Rect and a Size. The Rect (first argument)
/// and the RelativeRect (the output) are in the coordinate space of the
......@@ -56,15 +58,23 @@ class RelativeRect {
static final RelativeRect fill = const RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0);
/// Distance from the left side of the container to the left side of this rectangle.
///
/// May be negative if the left side of the rectangle is outside of the container.
final double left;
/// Distance from the top side of the container to the top side of this rectangle.
///
/// May be negative if the top side of the rectangle is outside of the container.
final double top;
/// Distance from the right side of the container to the right side of this rectangle.
///
/// May be negative if the right side of the rectangle is outside of the container.
final double right;
/// Distance from the bottom side of the container to the bottom side of this rectangle.
///
/// May be negative if the bottom side of the rectangle is outside of the container.
final double bottom;
/// Returns a new rectangle object translated by the given offset.
......
......@@ -1157,7 +1157,7 @@ class SemanticsConfiguration {
/// The reading direction is given by [textDirection].
///
/// See also:
///
///
/// * [decreasedValue], describes what [value] will be after performing
/// [SemanticsAction.decrease]
/// * [increasedValue], describes what [value] will be after performing
......
......@@ -684,7 +684,14 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
final Color shadowColor;
@override
RenderPhysicalModel createRenderObject(BuildContext context) => new RenderPhysicalModel(shape: shape, borderRadius: borderRadius, elevation: elevation, color: color, shadowColor: shadowColor);
RenderPhysicalModel createRenderObject(BuildContext context) {
return new RenderPhysicalModel(
shape: shape,
borderRadius: borderRadius,
elevation: elevation, color: color,
shadowColor: shadowColor,
);
}
@override
void updateRenderObject(BuildContext context, RenderPhysicalModel renderObject) {
......@@ -732,6 +739,7 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
/// ```
///
/// See also:
///
/// * [RotatedBox], which rotates the child widget during layout, not just
/// during painting.
/// * [FittedBox], which sizes and positions its child widget to fit the parent
......@@ -969,6 +977,7 @@ class CompositedTransformFollower extends SingleChildRenderObjectWidget {
///
/// * [Transform], which applies an arbitrary transform to its child widget at
/// paint time.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class FittedBox extends SingleChildRenderObjectWidget {
/// Creates a widget that scales and positions its child within itself according to [fit].
///
......@@ -1026,6 +1035,10 @@ class FittedBox extends SingleChildRenderObjectWidget {
/// Hit tests will only be detected inside the bounds of the
/// [FractionalTranslation], even if the contents are offset such that
/// they overflow.
///
/// See also:
///
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class FractionalTranslation extends SingleChildRenderObjectWidget {
/// Creates a widget that translates its child's painting.
///
......@@ -1048,7 +1061,12 @@ class FractionalTranslation extends SingleChildRenderObjectWidget {
final bool transformHitTests;
@override
RenderFractionalTranslation createRenderObject(BuildContext context) => new RenderFractionalTranslation(translation: translation, transformHitTests: transformHitTests);
RenderFractionalTranslation createRenderObject(BuildContext context) {
return new RenderFractionalTranslation(
translation: translation,
transformHitTests: transformHitTests,
);
}
@override
void updateRenderObject(BuildContext context, RenderFractionalTranslation renderObject) {
......@@ -1080,6 +1098,7 @@ class FractionalTranslation extends SingleChildRenderObjectWidget {
///
/// * [Transform], which is a paint effect that allows you to apply an
/// arbitrary transform to a child.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class RotatedBox extends SingleChildRenderObjectWidget {
/// A widget that rotates its child.
///
......@@ -1112,7 +1131,8 @@ class RotatedBox extends SingleChildRenderObjectWidget {
///
/// ## Sample code
///
/// This snippet indents the child (a [Card] with some [Text]) by eight pixels in each direction:
/// This snippet indents the child (a [Card] with some [Text]) by eight pixels
/// in each direction:
///
/// ```dart
/// new Padding(
......@@ -1139,13 +1159,12 @@ class RotatedBox extends SingleChildRenderObjectWidget {
///
/// In fact, the majority of widgets in Flutter are simply combinations of other
/// simpler widgets. Composition, rather than inheritance, is the primary
/// mechansim for building up widgets.
/// mechanism for building up widgets.
///
/// See also:
///
/// * [EdgeInsets], the class that is used to describe the padding dimensions.
/// * [Center], which positions the child at its natural dimensions, centered
/// in the parent.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Padding extends SingleChildRenderObjectWidget {
/// Creates a widget that insets its child.
///
......@@ -1203,8 +1222,9 @@ class Padding extends SingleChildRenderObjectWidget {
/// a single child.
/// * [Center], which is the same as [Align] but with the [alignment] always
/// set to [Alignment.center].
/// * [FractionallySizedBox], which sizes its child based on a fraction of its own
/// size and positions the child according to an [Alignment] value.
/// * [FractionallySizedBox], which sizes its child based on a fraction of its
/// own size and positions the child according to an [Alignment] value.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Align extends SingleChildRenderObjectWidget {
/// Creates an alignment widget.
///
......@@ -1283,6 +1303,11 @@ class Align extends SingleChildRenderObjectWidget {
///
/// * [Align], which lets you arbitrarily position a child within itself,
/// rather than just centering it.
/// * [Row], a widget that displays its children in a horizontal array.
/// * [Column], a widget that displays its children in a vertical array.
/// * [Container], a convenience widget that combines common painting,
/// positioning, and sizing widgets.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Center extends Align {
/// Creates a widget that centers its child.
const Center({ Key key, double widthFactor, double heightFactor, Widget child })
......@@ -1320,7 +1345,9 @@ class CustomSingleChildLayout extends SingleChildRenderObjectWidget {
final SingleChildLayoutDelegate delegate;
@override
RenderCustomSingleChildLayoutBox createRenderObject(BuildContext context) => new RenderCustomSingleChildLayoutBox(delegate: delegate);
RenderCustomSingleChildLayoutBox createRenderObject(BuildContext context) {
return new RenderCustomSingleChildLayoutBox(delegate: delegate);
}
@override
void updateRenderObject(BuildContext context, RenderCustomSingleChildLayoutBox renderObject) {
......@@ -1448,12 +1475,15 @@ class CustomMultiChildLayout extends MultiChildRenderObjectWidget {
///
/// * [ConstrainedBox], a more generic version of this class that takes
/// arbitrary [BoxConstraints] instead of an explicit width and height.
/// * [UnconstrainedBox], a container that tries to let its child draw without
/// constraints.
/// * [FractionallySizedBox], a widget that sizes its child to a fraction of
/// the total available space.
/// * [AspectRatio], a widget that attempts to fit within the parent's
/// constraints while also sizing its child to match a given sapect ratio.
/// * [FittedBox], which sizes and positions its child widget to fit the parent
/// according to a given [BoxFit] discipline.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class SizedBox extends SingleChildRenderObjectWidget {
/// Creates a fixed size box. The [width] and [height] parameters can be null
/// to indicate that the size of the box should not be constrained in
......@@ -1505,7 +1535,9 @@ class SizedBox extends SingleChildRenderObjectWidget {
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
final DiagnosticLevel level = (width == double.INFINITY && height == double.INFINITY) ? DiagnosticLevel.hidden : DiagnosticLevel.info;
final DiagnosticLevel level = (width == double.INFINITY && height == double.INFINITY)
? DiagnosticLevel.hidden
: DiagnosticLevel.info;
description.add(new DoubleProperty('width', width, defaultValue: null, level: level));
description.add(new DoubleProperty('height', height, defaultValue: null, level: level));
}
......@@ -1534,12 +1566,15 @@ class SizedBox extends SingleChildRenderObjectWidget {
/// See also:
///
/// * [BoxConstraints], the class that describes constraints.
/// * [UnconstrainedBox], a container that tries to let its child draw without
/// constraints.
/// * [SizedBox], which lets you specify tight constraints by explicitly
/// specifying the height or width.
/// * [FractionallySizedBox], a widget that sizes its child to a fraction of
/// the total available space.
/// * [FractionallySizedBox], which sizes its child based on a fraction of its
/// own size and positions the child according to an [Alignment] value.
/// * [AspectRatio], a widget that attempts to fit within the parent's
/// constraints while also sizing its child to match a given sapect ratio.
/// constraints while also sizing its child to match a given aspect ratio.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class ConstrainedBox extends SingleChildRenderObjectWidget {
/// Creates a widget that imposes additional constraints on its child.
///
......@@ -1556,7 +1591,9 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
final BoxConstraints constraints;
@override
RenderConstrainedBox createRenderObject(BuildContext context) => new RenderConstrainedBox(additionalConstraints: constraints);
RenderConstrainedBox createRenderObject(BuildContext context) {
return new RenderConstrainedBox(additionalConstraints: constraints);
}
@override
void updateRenderObject(BuildContext context, RenderConstrainedBox renderObject) {
......@@ -1570,15 +1607,87 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
}
}
/// A widget that imposes no constraints on its child, allowing it to render
/// at its "natural" size.
///
/// This allows a child to render at the size it would render if it were alone
/// on an infinite canvas with no constraints. This container will then expand
/// as much as it can within its own constraints and align the child based on
/// [alignment]. If the container cannot expand enough to accommodate the
/// entire child, the child will be clipped.
///
/// In debug mode, if the child overflows the container, a warning will be
/// printed on the console, and black and yellow striped areas will appear where
/// the overflow occurs.
///
/// See also:
///
/// * [ConstrainedBox] for a box which imposes constraints on its child.
/// * [Container], a convenience widget that combines common painting,
/// positioning, and sizing widgets.
/// * [OverflowBox], a widget that imposes different constraints on its child
/// than it gets from its parent, possibly allowing the child to overflow
/// the parent.
class UnconstrainedBox extends SingleChildRenderObjectWidget {
/// Creates a widget that imposes no constraints on its child, allowing it to
/// render at its "natural" size. If the child overflows the parents
/// constraints, a warning will be given in debug mode.
const UnconstrainedBox({
Key key,
Widget child,
this.textDirection,
this.alignment: Alignment.center,
}) : assert(alignment != null),
super(key: key, child: child);
/// The text direction to use when interpreting the [alignment] if it is an
/// [AlignmentDirectional].
final TextDirection textDirection;
/// The alignment to use when laying out the child.
///
/// If this is an [AlignmentDirectional], then [textDirection] must not be
/// null.
///
/// See also:
///
/// * [Alignment] for non-[Directionality]-aware alignments.
/// * [AlignmentDirectional] for [Directionality]-aware alignments.
final AlignmentGeometry alignment;
@override
void updateRenderObject(BuildContext context, covariant RenderUnconstrainedBox renderObject) {
renderObject
..textDirection = textDirection ?? Directionality.of(context)
..alignment = alignment;
}
@override
RenderUnconstrainedBox createRenderObject(BuildContext context) => new RenderUnconstrainedBox(
textDirection: textDirection ?? Directionality.of(context),
alignment: alignment,
);
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description.add(new DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
description.add(new DiagnosticsProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
}
}
/// A widget that sizes its child to a fraction of the total available space.
/// For more details about the layout algorithm, see
/// [RenderFractionallySizedOverflowBox].
///
/// See also:
///
/// * [Align] (which sizes itself based on its child's size and positions
/// the child according to an [Alignment] value)
/// * [OverflowBox]
/// * [Align], which sizes itself based on its child's size and positions
/// the child according to an [Alignment] value.
/// * [OverflowBox], a widget that imposes different constraints on its child
/// than it gets from its parent, possibly allowing the child to overflow the
/// parent.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class FractionallySizedBox extends SingleChildRenderObjectWidget {
/// Creates a widget that sizes its child to a fraction of the total available space.
///
......@@ -1674,6 +1783,7 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget {
/// when the incoming constraints are unbounded.
/// * [SizedBox], which lets you specify tight constraints by explicitly
/// specifying the height or width.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class LimitedBox extends SingleChildRenderObjectWidget {
/// Creates a box that limits its size only when it's unconstrained.
///
......@@ -1722,7 +1832,18 @@ class LimitedBox extends SingleChildRenderObjectWidget {
/// A widget that imposes different constraints on its child than it gets
/// from its parent, possibly allowing the child to overflow the parent.
///
/// See [RenderConstrainedOverflowBox] for details.
/// See also:
///
/// * [RenderConstrainedOverflowBox] for details about how [OverflowBox] is
/// rendered.
/// * [SizedOverflowBox], a widget that is a specific size but passes its
/// original constraints through to its child, which may then overflow.
/// * [ConstrainedBox], a widget that imposes additional constraints on its
/// child.
/// * [UnconstrainedBox], a container that tries to let its child draw without
/// constraints.
/// * [SizedBox], a box with a specified size.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class OverflowBox extends SingleChildRenderObjectWidget {
/// Creates a widget that lets its child overflow itself.
const OverflowBox({
......@@ -1797,7 +1918,18 @@ class OverflowBox extends SingleChildRenderObjectWidget {
}
/// A widget that is a specific size but passes its original constraints
/// through to its child, which will probably overflow.
/// through to its child, which may then overflow.
///
/// See also:
///
/// * [OverflowBox], A widget that imposes different constraints on its child
/// than it gets from its parent, possibly allowing the child to overflow the
/// parent.
/// * [ConstrainedBox], a widget that imposes additional constraints on its
/// child.
/// * [UnconstrainedBox], a container that tries to let its child draw without
/// constraints.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class SizedOverflowBox extends SingleChildRenderObjectWidget {
/// Creates a widget of a given size that lets its child overflow.
///
......@@ -1853,6 +1985,10 @@ class SizedOverflowBox extends SingleChildRenderObjectWidget {
/// A widget that lays the child out as if it was in the tree, but without painting anything,
/// without making the child available for hit testing, and without taking any
/// room in the parent.
///
/// See also:
///
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Offstage extends SingleChildRenderObjectWidget {
/// Creates a widget that visually hides its child.
const Offstage({ Key key, this.offstage: true, Widget child })
......@@ -1925,6 +2061,16 @@ class _OffstageElement extends SingleChildRenderObjectElement {
/// find a feasible size after consulting each constraint, the widget
/// will eventually select a size for the child that meets the layout
/// constraints but fails to meet the aspect ratio constraints.
///
/// See also:
///
/// * [Align], a widget that aligns its child within itself and optionally
/// sizes itself based on the child's size.
/// * [ConstrainedBox], a widget that imposes additional constraints on its
/// child.
/// * [UnconstrainedBox], a container that tries to let its child draw without
/// constraints.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class AspectRatio extends SingleChildRenderObjectWidget {
/// Creates a widget with a specific aspect ratio.
///
......@@ -1972,6 +2118,10 @@ class AspectRatio extends SingleChildRenderObjectWidget {
/// pass before the final layout phase. Avoid using it where possible. In the
/// worst case, this widget can result in a layout that is O(N²) in the depth of
/// the tree.
///
/// See also:
///
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class IntrinsicWidth extends SingleChildRenderObjectWidget {
/// Creates a widget that sizes its child to the child's intrinsic width.
///
......@@ -1986,7 +2136,9 @@ class IntrinsicWidth extends SingleChildRenderObjectWidget {
final double stepHeight;
@override
RenderIntrinsicWidth createRenderObject(BuildContext context) => new RenderIntrinsicWidth(stepWidth: stepWidth, stepHeight: stepHeight);
RenderIntrinsicWidth createRenderObject(BuildContext context) {
return new RenderIntrinsicWidth(stepWidth: stepWidth, stepHeight: stepHeight);
}
@override
void updateRenderObject(BuildContext context, RenderIntrinsicWidth renderObject) {
......@@ -2006,6 +2158,10 @@ class IntrinsicWidth extends SingleChildRenderObjectWidget {
/// pass before the final layout phase. Avoid using it where possible. In the
/// worst case, this widget can result in a layout that is O(N²) in the depth of
/// the tree.
///
/// See also:
///
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class IntrinsicHeight extends SingleChildRenderObjectWidget {
/// Creates a widget that sizes its child to the child's intrinsic height.
///
......@@ -2024,6 +2180,13 @@ class IntrinsicHeight extends SingleChildRenderObjectWidget {
/// contain the child. If [baseline] is less than the distance from
/// the top of the child to the baseline of the child, then the child
/// is top-aligned instead.
///
/// See also:
///
/// * [Align], a widget that aligns its child within itself and optionally
/// sizes itself based on the child's size.
/// * [Center], a widget that centers its child within itself.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Baseline extends SingleChildRenderObjectWidget {
/// Creates a widget that positions its child according to the child's baseline.
///
......@@ -2045,7 +2208,9 @@ class Baseline extends SingleChildRenderObjectWidget {
final TextBaseline baselineType;
@override
RenderBaseline createRenderObject(BuildContext context) => new RenderBaseline(baseline: baseline, baselineType: baselineType);
RenderBaseline createRenderObject(BuildContext context) {
return new RenderBaseline(baseline: baseline, baselineType: baselineType);
}
@override
void updateRenderObject(BuildContext context, RenderBaseline renderObject) {
......@@ -2285,6 +2450,7 @@ class ListBody extends MultiChildRenderObjectWidget {
/// children.
/// * [Flow], which provides paint-time control of its children using transform
/// matrices.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Stack extends MultiChildRenderObjectWidget {
/// Creates a stack layout widget.
///
......@@ -2367,7 +2533,10 @@ class Stack extends MultiChildRenderObjectWidget {
///
/// If value is null, then nothing is displayed.
///
/// For more details, see [Stack].
/// See also:
///
/// * [Stack], for more details about stacks.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class IndexedStack extends Stack {
/// Creates a [Stack] widget that paints a single child.
///
......@@ -2838,6 +3007,7 @@ class PositionedDirectional extends StatelessWidget {
/// * [Expanded], to indicate children that should take all the remaining room.
/// * [Flexible], to indicate children that should share the remaining room but
/// that may be sized smaller (leaving some remaining room unused).
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Flex extends MultiChildRenderObjectWidget {
/// Creates a flex layout.
///
......@@ -3168,6 +3338,7 @@ class Flex extends MultiChildRenderObjectWidget {
/// * [Expanded], to indicate children that should take all the remaining room.
/// * [Flexible], to indicate children that should share the remaining room but
/// that may by sized smaller (leaving some remaining room unused).
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Row extends Flex {
/// Creates a horizontal array of children.
///
......@@ -3359,6 +3530,7 @@ class Row extends Flex {
/// * [Expanded], to indicate children that should take all the remaining room.
/// * [Flexible], to indicate children that should share the remaining room but
/// that may size smaller (leaving some remaining room unused).
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Column extends Flex {
/// Creates a vertical array of children.
///
......@@ -3409,6 +3581,7 @@ class Column extends Flex {
/// See also:
///
/// * [Expanded], which forces the child to expand to fill the available space.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Flexible extends ParentDataWidget<Flex> {
/// Creates a widget that controls how a child of a [Row], [Column], or [Flex]
/// flexes.
......@@ -3481,6 +3654,7 @@ class Flexible extends ParentDataWidget<Flex> {
/// See also:
///
/// * [Flexible], which does not force the child to fill the available space.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Expanded extends Flexible {
/// Creates a widget that expands a child of a [Row], [Column], or [Flex]
/// expand to fill the available space in the main axis.
......@@ -3539,6 +3713,7 @@ class Expanded extends Flexible {
///
/// * [Row], which places children in one line, and gives control over their
/// alignment and spacing.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Wrap extends MultiChildRenderObjectWidget {
/// Creates a wrap layout.
///
......@@ -3769,6 +3944,7 @@ class Wrap extends MultiChildRenderObjectWidget {
/// a single child.
/// * [CustomMultiChildLayout], which uses a delegate to position multiple
/// children.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Flow extends MultiChildRenderObjectWidget {
/// Creates a flow layout.
///
......@@ -4577,6 +4753,7 @@ class Semantics extends SingleChildRenderObjectWidget {
/// or an explicit [textDirection] should be provided.
///
/// See also:
///
/// * [SemanticsConfiguration.label] for a description of how this is exposed
/// in TalkBack and VoiceOver.
final String label;
......@@ -4587,6 +4764,7 @@ class Semantics extends SingleChildRenderObjectWidget {
/// or an explicit [textDirection] should be provided.
///
/// See also:
///
/// * [SemanticsConfiguration.value] for a description of how this is exposed
/// in TalkBack and VoiceOver.
final String value;
......@@ -4599,6 +4777,7 @@ class Semantics extends SingleChildRenderObjectWidget {
/// must be provided.
///
/// See also:
///
/// * [SemanticsConfiguration.increasedValue] for a description of how this
/// is exposed in TalkBack and VoiceOver.
final String increasedValue;
......@@ -4611,6 +4790,7 @@ class Semantics extends SingleChildRenderObjectWidget {
/// must be provided.
///
/// See also:
///
/// * [SemanticsConfiguration.decreasedValue] for a description of how this
/// is exposed in TalkBack and VoiceOver.
final String decreasedValue;
......@@ -4622,6 +4802,7 @@ class Semantics extends SingleChildRenderObjectWidget {
/// or an explicit [textDirection] should be provided.
///
/// See also:
///
/// * [SemanticsConfiguration.hint] for a description of how this is exposed
/// in TalkBack and VoiceOver.
final String hint;
......
......@@ -224,6 +224,7 @@ class DecoratedBox extends SingleChildRenderObjectWidget {
/// * [AnimatedContainer], a variant that smoothly animates the properties when
/// they change.
/// * [Border], which has a sample which uses [Container] heavily.
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class Container extends StatelessWidget {
/// Creates a widget that combines common painting, positioning, and sizing widgets.
///
......
......@@ -41,6 +41,7 @@ class BoxConstraintsTween extends Tween<BoxConstraints> {
/// interpolation between decorations.
///
/// See also:
///
/// * [Tween] for a discussion on how to use interpolation objects.
/// * [ShapeDecoration], [RoundedRectangleBorder], [CircleBorder], and
/// [StadiumBorder] for examples of shape borders that can be smoothly
......@@ -346,6 +347,7 @@ abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> exten
///
/// * [AnimatedPadding], which is a subset of this widget that only
/// supports animating the [padding].
/// * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
class AnimatedContainer extends ImplicitlyAnimatedWidget {
/// Creates a container that animates its parameters implicitly.
///
......
......@@ -152,7 +152,7 @@ void main() {
));
// the column overflows because we're forcing it to 600 pixels high
expect(tester.takeException(), contains('A vertical RenderFlex overflowed by'));
expect(tester.takeException(), contains('A RenderFlex overflowed by'));
expect(find.text('Gingerbread (0)'), findsOneWidget);
expect(find.text('Gingerbread (1)'), findsNothing);
......
......@@ -128,4 +128,78 @@ void main() {
layout(constraintedBox);
expect(coloredBox.parentData?.runtimeType, ParentData);
});
test('UnconstrainedBox expands to fit children', () {
final RenderUnconstrainedBox unconstrained = new RenderUnconstrainedBox(
textDirection: TextDirection.ltr,
child: new RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 200.0, height: 200.0),
),
alignment: Alignment.center,
);
layout(
unconstrained,
constraints: const BoxConstraints(
minWidth: 200.0,
maxWidth: 200.0,
minHeight: 200.0,
maxHeight: 200.0,
),
);
expect(unconstrained.size.width, equals(200.0), reason: 'unconstrained width');
expect(unconstrained.size.height, equals(200.0), reason: 'unconstrained height');
});
test('UnconstrainedBox handles vertical overflow', () {
final RenderUnconstrainedBox unconstrained = new RenderUnconstrainedBox(
textDirection: TextDirection.ltr,
child: new RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(height: 200.0),
),
alignment: Alignment.center,
);
final BoxConstraints viewport = const BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
layout(unconstrained, constraints: viewport);
expect(unconstrained.getMinIntrinsicHeight(100.0), equals(200.0));
expect(unconstrained.getMaxIntrinsicHeight(100.0), equals(200.0));
expect(unconstrained.getMinIntrinsicWidth(100.0), equals(0.0));
expect(unconstrained.getMaxIntrinsicWidth(100.0), equals(0.0));
});
test('UnconstrainedBox handles horizontal overflow', () {
final RenderUnconstrainedBox unconstrained = new RenderUnconstrainedBox(
textDirection: TextDirection.ltr,
child: new RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 200.0),
),
alignment: Alignment.center,
);
final BoxConstraints viewport = const BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
layout(unconstrained, constraints: viewport);
expect(unconstrained.getMinIntrinsicHeight(100.0), equals(0.0));
expect(unconstrained.getMaxIntrinsicHeight(100.0), equals(0.0));
expect(unconstrained.getMinIntrinsicWidth(100.0), equals(200.0));
expect(unconstrained.getMaxIntrinsicWidth(100.0), equals(200.0));
});
test('UnconstrainedBox.toStringDeep returns useful information', () {
final RenderUnconstrainedBox unconstrained = new RenderUnconstrainedBox(
textDirection: TextDirection.ltr,
alignment: Alignment.center,
);
expect(unconstrained.alignment, Alignment.center);
expect(unconstrained.textDirection, TextDirection.ltr);
expect(unconstrained, hasAGoodToStringDeep);
expect(
unconstrained.toStringDeep(minLevel: DiagnosticLevel.info),
equalsIgnoringHashCodes(
'RenderUnconstrainedBox#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
' parentData: MISSING\n'
' constraints: MISSING\n'
' size: MISSING\n'
' alignment: Alignment.center\n'
' textDirection: ltr\n'),
);
});
}
// Copyright 2017 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 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import '../rendering/mock_canvas.dart';
void main() {
testWidgets('overflow indicator is not shown when not overflowing', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: const UnconstrainedBox(
child: const SizedBox(width: 200.0, height: 200.0),
),
),
);
expect(find.byType(UnconstrainedBox), isNot(paints..rect()));
});
testWidgets('overflow indicator is shown when overflowing', (WidgetTester tester) async {
final UnconstrainedBox box = const UnconstrainedBox(
child: const SizedBox(width: 200.0, height: 200.0),
);
await tester.pumpWidget(
new Center(
child: new SizedBox(
height: 100.0,
child: box,
),
),
);
expect(tester.takeException(), contains('A RenderUnconstrainedBox overflowed by'));
expect(find.byType(UnconstrainedBox), paints..rect());
await tester.pumpWidget(
new Center(
child: new SizedBox(
height: 100.0,
child: box,
),
),
);
// Doesn't throw the exception a second time, because we didn't reset
// overflowReportNeeded.
expect(tester.takeException(), isNull);
expect(find.byType(UnconstrainedBox), paints..rect());
});
testWidgets('overflow indicator is not shown when constraint size is zero.', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: const SizedBox(
height: 0.0,
child: const UnconstrainedBox(
child: const SizedBox(width: 200.0, height: 200.0),
),
),
),
);
expect(find.byType(UnconstrainedBox), isNot(paints..rect()));
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment